<!DOCTYPE html>












  




<html class="theme-next gemini use-motion" lang="zh-CN">
<head>
  <!-- hexo-inject:begin --><!-- hexo-inject:end --><meta charset="UTF-8"/>
<meta name="google-site-verification" content="o9IkI77-fxkhBZW-n0ww9JALMCqdDbeTgdcXO_Bw4Zc" />
<meta name="baidu-site-verification" content="3frqY9KiVO" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2"/>
<meta name="theme-color" content="#222">



  
  
  <link rel="stylesheet" href="/lib/needsharebutton/needsharebutton.css">










<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />



















  
  
  
  

  
    
    
  

  
    
      
    

    
  

  

  
    
      
    

    
  

  
    
      
    

    
  

  
    
    
    <link href="//fonts.googleapis.com/css?family=Monda:300,300italic,400,400italic,700,700italic|Roboto Slab:300,300italic,400,400italic,700,700italic|Lobster Two:300,300italic,400,400italic,700,700italic|PT Mono:300,300italic,400,400italic,700,700italic&subset=latin,latin-ext" rel="stylesheet" type="text/css">
  






<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />

<link href="/css/main.css?v=6.4.1" rel="stylesheet" type="text/css" />


  <link rel="apple-touch-icon" sizes="180x180" href="/images/logo.png?v=6.4.1">


  <link rel="icon" type="image/png" sizes="32x32" href="/images/logo.png?v=6.4.1">


  <link rel="icon" type="image/png" sizes="16x16" href="/images/logo.png?v=6.4.1">


  <link rel="mask-icon" href="/images/logo.svg?v=6.4.1" color="#222">









<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/',
    scheme: 'Gemini',
    version: '6.4.1',
    sidebar: {"position":"left","display":"post","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: false,
    fastclick: false,
    lazyload: false,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>


  




  <meta name="description" content="摘要：日常学习中对一些知识点进行总结得出该系列文章。学习笔记内容包括前端技术，Django web开发技术，数据库技术如MySQL，MongoDB，PGSQL等等。此外还有一些工具如Dock，ES等等。（本文原创，转载必须注明出处.）">
<meta name="keywords" content="django">
<meta property="og:type" content="article">
<meta property="og:title" content="笔记9:Django提升篇">
<meta property="og:url" content="https://bainingchao.github.io/2019/06/13/笔记9-Django提升篇/index.html">
<meta property="og:site_name" content="白宁超的官网">
<meta property="og:description" content="摘要：日常学习中对一些知识点进行总结得出该系列文章。学习笔记内容包括前端技术，Django web开发技术，数据库技术如MySQL，MongoDB，PGSQL等等。此外还有一些工具如Dock，ES等等。（本文原创，转载必须注明出处.）">
<meta property="og:locale" content="zh-CN">
<meta property="og:image" content="https://github.com/bainingchao/imgurl/blob/master/dockerimg/1560310208431.png?raw=true">
<meta property="og:updated_time" content="2019-06-13T11:10:22.269Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="笔记9:Django提升篇">
<meta name="twitter:description" content="摘要：日常学习中对一些知识点进行总结得出该系列文章。学习笔记内容包括前端技术，Django web开发技术，数据库技术如MySQL，MongoDB，PGSQL等等。此外还有一些工具如Dock，ES等等。（本文原创，转载必须注明出处.）">
<meta name="twitter:image" content="https://github.com/bainingchao/imgurl/blob/master/dockerimg/1560310208431.png?raw=true">



  <link rel="alternate" href="/atom.xml" title="白宁超的官网" type="application/atom+xml" />




  <link rel="canonical" href="https://bainingchao.github.io/2019/06/13/笔记9-Django提升篇/"/>



<script type="text/javascript" id="page.configurations">
  CONFIG.page = {
    sidebar: "",
  };
</script>

  <title>笔记9:Django提升篇 | 白宁超的官网</title>
  









  <noscript>
  <style type="text/css">
    .use-motion .motion-element,
    .use-motion .brand,
    .use-motion .menu-item,
    .sidebar-inner,
    .use-motion .post-block,
    .use-motion .pagination,
    .use-motion .comments,
    .use-motion .post-header,
    .use-motion .post-body,
    .use-motion .collection-title { opacity: initial; }

    .use-motion .logo,
    .use-motion .site-title,
    .use-motion .site-subtitle {
      opacity: initial;
      top: initial;
    }

    .use-motion {
      .logo-line-before i { left: initial; }
      .logo-line-after i { right: initial; }
    }
  </style>
</noscript><!-- hexo-inject:begin --><!-- hexo-inject:end -->

</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-CN">

  
  
    
  

  <!-- hexo-inject:begin --><!-- hexo-inject:end --><div class="container sidebar-position-left page-post-detail">
    <div class="headband"></div>

	<!-- <a href="https://github.com/bainingchao"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png" alt="Fork me on GitHub"></a> !-->
	
    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/" class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">白宁超的官网</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
    
      
        <h1 class="site-subtitle" itemprop="description">专注人工智能领域研究</h1>
      
    
  </div>

  <div class="site-nav-toggle">
    <button aria-label="切换导航栏">
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>



<nav class="site-nav">
  
    <ul id="menu" class="menu">
      
        
        
        
          
          <li class="menu-item menu-item-首页">
    <a href="/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-home"></i> <br />首页</a>
  </li>
        
        
        
          
          <li class="menu-item menu-item-标签">
    <a href="/tags/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-tags"></i> <br />标签</a>
  </li>
        
        
        
          
          <li class="menu-item menu-item-分类">
    <a href="/categories/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-th"></i> <br />分类</a>
  </li>
        
        
        
          
          <li class="menu-item menu-item-归档">
    <a href="/archives/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />归档</a>
  </li>
        
        
        
          
          <li class="menu-item menu-item-视频">
    <a href="/videos/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-sitemap"></i> <br />视频</a>
  </li>
        
        
        
          
          <li class="menu-item menu-item-书籍">
    <a href="/books/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-th"></i> <br />书籍</a>
  </li>
        
        
        
          
          <li class="menu-item menu-item-链接">
    <a href="/links/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-question-circle"></i> <br />链接</a>
  </li>
        
        
        
          
          <li class="menu-item menu-item-关于">
    <a href="/about/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-user"></i> <br />关于</a>
  </li>

      
      
        <li class="menu-item menu-item-search">
          
            <a href="javascript:;" class="popup-trigger">
          
            
              <i class="menu-item-icon fa fa-search fa-fw"></i> <br />搜索</a>
        </li>
      
    </ul>
  

  

  
    <div class="site-search">
      
  <div class="popup search-popup local-search-popup">
  <div class="local-search-header clearfix">
    <span class="search-icon">
      <i class="fa fa-search"></i>
    </span>
    <span class="popup-btn-close">
      <i class="fa fa-times-circle"></i>
    </span>
    <div class="local-search-input-wrapper">
      <input autocomplete="off"
             placeholder="搜索..." spellcheck="false"
             type="text" id="local-search-input">
    </div>
  </div>
  <div id="local-search-result"></div>
</div>



    </div>
  
</nav>



  



</div>
    </header>

    


    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          
            

          
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="https://bainingchao.github.io/2019/06/13/笔记9-Django提升篇/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="白宁超">
      <meta itemprop="description" content="本站主要研究深度学习、机器学习、自然语言处理等前沿技术。ML&NLP交流群：436303759 <span><a target="_blank" href="http://shang.qq.com/wpa/qunwpa?idkey=ef3bbb679b06ac59b136c57ba9e7935ff9d3b10faeabde6e4efcafe523bbbf4d"><img border="0" src="http://pub.idqqimg.com/wpa/images/group.png" alt="自然语言处理和机器学习技术QQ交流：436303759 " title="自然语言处理和机器学习技术交流"></a></span>">
      <meta itemprop="image" content="/../images/header.png">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="白宁超的官网">
    </span>

    
      <header class="post-header">

        
        
          <h2 class="post-title" itemprop="name headline">笔记9:Django提升篇
              
            
          </h2>
        

        <div class="post-meta">
          <span class="post-time">

            
            
            

            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              

              
                
              

              <time title="创建时间：2019-06-13 19:09:25 / 修改时间：19:10:22" itemprop="dateCreated datePublished" datetime="2019-06-13T19:09:25+08:00">2019-06-13</time>
            

            
              

              
            
          </span>

          
            <span class="post-category" >
            
              <span class="post-meta-divider">|</span>
            
              <span class="post-meta-item-icon">
                <i class="fa fa-folder-o"></i>
              </span>
              
                <span class="post-meta-item-text">分类于</span>
              
              
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/categories/学习笔记/" itemprop="url" rel="index"><span itemprop="name">学习笔记</span></a></span>

                
                
              
            </span>
          

          
            
          

          
          

          
            <span class="post-meta-divider">|</span>
            <span class="post-meta-item-icon"
            >
            <i class="fa fa-eye"></i>
             阅读次数： 
            <span class="busuanzi-value" id="busuanzi_value_page_pv" ></span>
            </span>
          
		  

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body" itemprop="articleBody">

      
      

      
        <blockquote>
<p>摘要：日常学习中对一些知识点进行总结得出该系列文章。学习笔记内容包括前端技术，Django web开发技术，数据库技术如MySQL，MongoDB，PGSQL等等。此外还有一些工具如Dock，ES等等。（本文原创，转载必须注明出处.）</p>
</blockquote>
<a id="more"></a>
<p>（1）打包应用程序：可重用性</p>
<ul>
<li>打包 Python 程序需要工具：<a href="https://pypi.org/project/setuptools/" target="_blank" rel="noopener">setuptools</a> 。打包时候建议使用django-appname</li>
<li>1 在你的 Django 项目目录外创建一个名为 <code>django-polls</code> 的文件夹，用于盛放 <code>polls</code>。</li>
<li>2 将 <code>polls</code> 目录移入 <code>django-polls</code> 目录。</li>
<li>3 创建一个名为 <code>django-polls/README.rst</code> 的文件，包含以下内容：</li>
</ul>
<figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line">=====</span><br><span class="line">Polls</span><br><span class="line">=====</span><br><span class="line"></span><br><span class="line">Polls is a simple Django app to conduct Web-based polls. For each</span><br><span class="line">question, visitors can choose between a fixed number of answers.</span><br><span class="line"></span><br><span class="line">Detailed documentation is in the "docs" directory.</span><br><span class="line"></span><br><span class="line">Quick <span class="keyword">start</span></span><br><span class="line"><span class="comment">-----------</span></span><br><span class="line"></span><br><span class="line"><span class="number">1.</span> <span class="keyword">Add</span> <span class="string">"polls"</span> <span class="keyword">to</span> your INSTALLED_APPS setting <span class="keyword">like</span> this::</span><br><span class="line"></span><br><span class="line">    INSTALLED_APPS = [</span><br><span class="line">        ...</span><br><span class="line">        <span class="string">'polls'</span>,</span><br><span class="line">    ]</span><br><span class="line"></span><br><span class="line"><span class="number">2.</span> <span class="keyword">Include</span> the polls URLconf <span class="keyword">in</span> your <span class="keyword">project</span> urls.py <span class="keyword">like</span> this::</span><br><span class="line"></span><br><span class="line">    <span class="keyword">path</span>(<span class="string">'polls/'</span>, <span class="keyword">include</span>(<span class="string">'polls.urls'</span>)),</span><br><span class="line"></span><br><span class="line"><span class="number">3.</span> Run <span class="string">`python manage.py migrate`</span> <span class="keyword">to</span> <span class="keyword">create</span> the polls models.</span><br><span class="line"></span><br><span class="line"><span class="number">4.</span> <span class="keyword">Start</span> the development <span class="keyword">server</span> <span class="keyword">and</span> visit <span class="keyword">http</span>://<span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span>:<span class="number">8000</span>/<span class="keyword">admin</span>/</span><br><span class="line">   <span class="keyword">to</span> <span class="keyword">create</span> a poll (you<span class="string">'ll need the Admin app enabled).</span></span><br><span class="line"><span class="string"></span></span><br><span class="line"><span class="string">5. Visit http://127.0.0.1:8000/polls/ to participate in the poll.</span></span><br></pre></td></tr></table></figure>
<ul>
<li>4 创建一个 <code>django-polls/LICENSE</code> 文件。选择一个非本教程使用的授权协议，但是要足以说明发布代码没有授权证书是 <em>不可能的</em> 。Django 和很多兼容 Django 的应用是以 BSD 授权协议发布的；不过，你可以自己选择一个授权协议。只要确定你选择的协议能够限制未来会使用你的代码的人。</li>
<li>下一步我们将创建 <code>setup.py</code> 用于说明如何构建和安装应用的细节。关于此文件的完整介绍超出了此教程的范围，但是 <a href="https://setuptools.readthedocs.io/en/latest/" target="_blank" rel="noopener">setuptools docs</a> 有详细的介绍。创建文件 <code>django-polls/setup.py</code> 包含以下内容：</li>
</ul>
<figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line">import <span class="built_in">os</span></span><br><span class="line">from setuptools import find_packages, setup</span><br><span class="line"></span><br><span class="line">with <span class="built_in">open</span>(<span class="built_in">os</span>.<span class="built_in">path</span>.join(<span class="built_in">os</span>.<span class="built_in">path</span>.dirname(__file__), <span class="string">'README.rst'</span>)) as readme:</span><br><span class="line">    README = readme.<span class="built_in">read</span>()</span><br><span class="line"></span><br><span class="line"># allow setup.py to be run from any <span class="built_in">path</span></span><br><span class="line"><span class="built_in">os</span>.chdir(<span class="built_in">os</span>.<span class="built_in">path</span>.normpath(<span class="built_in">os</span>.<span class="built_in">path</span>.join(<span class="built_in">os</span>.<span class="built_in">path</span>.abspath(__file__), <span class="built_in">os</span>.pardir)))</span><br><span class="line"></span><br><span class="line">setup(</span><br><span class="line">    name=<span class="string">'django-polls'</span>,</span><br><span class="line">    version=<span class="string">'0.1'</span>,</span><br><span class="line">    packages=find_packages(),</span><br><span class="line">    include_package_data=True,</span><br><span class="line">    license=<span class="string">'BSD License'</span>,  # example license</span><br><span class="line">    description=<span class="string">'A simple Django app to conduct Web-based polls.'</span>,</span><br><span class="line">    long_description=README,</span><br><span class="line">    url=<span class="string">'https://www.example.com/'</span>,</span><br><span class="line">    author=<span class="string">'Your Name'</span>,</span><br><span class="line">    author_email=<span class="string">'yourname@example.com'</span>,</span><br><span class="line">    classifiers=[</span><br><span class="line">        <span class="string">'Environment :: Web Environment'</span>,</span><br><span class="line">        <span class="string">'Framework :: Django'</span>,</span><br><span class="line">        <span class="string">'Framework :: Django :: X.Y'</span>,  # replace <span class="string">"X.Y"</span> as appropriate</span><br><span class="line">        <span class="string">'Intended Audience :: Developers'</span>,</span><br><span class="line">        <span class="string">'License :: OSI Approved :: BSD License'</span>,  # example license</span><br><span class="line">        <span class="string">'Operating System :: OS Independent'</span>,</span><br><span class="line">        <span class="string">'Programming Language :: Python'</span>,</span><br><span class="line">        <span class="string">'Programming Language :: Python :: 3.5'</span>,</span><br><span class="line">        <span class="string">'Programming Language :: Python :: 3.6'</span>,</span><br><span class="line">        <span class="string">'Topic :: Internet :: WWW/HTTP'</span>,</span><br><span class="line">        <span class="string">'Topic :: Internet :: WWW/HTTP :: Dynamic Content'</span>,</span><br><span class="line">    ],</span><br><span class="line">)</span><br></pre></td></tr></table></figure>
<ul>
<li>6 默认包中只包含 Python 模块和包。为了包含额外文件，我们需要创建一个名为 <code>MANIFEST.in</code> 的文件。上一步中关于 setuptools 的文档详细介绍了这个文件。为了包含模板、<code>README.rst</code> 和我们的 <code>LICENSE</code> 文件，创建文件 <code>django-polls/MANIFEST.in</code> 包含以下内容：</li>
</ul>
<figure class="highlight fortran"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">include</span> LICENSE</span><br><span class="line"><span class="keyword">include</span> README.rst</span><br><span class="line"><span class="keyword">recursive</span>-<span class="keyword">include</span> polls/static *</span><br><span class="line"><span class="keyword">recursive</span>-<span class="keyword">include</span> polls/templates *</span><br></pre></td></tr></table></figure>
<ul>
<li>7 在应用中包含详细文档是可选的，但我们推荐你这样做。创建一个空目录 <code>django-polls/docs</code> 用于未来编写文档。额外添加一行至 <code>django-polls/MANIFEST.in</code></li>
</ul>
<figure class="highlight fortran"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">recursive</span>-<span class="keyword">include</span> docs *</span><br></pre></td></tr></table></figure>
<ul>
<li>8 试着构建你自己的应用包通过 <code>ptyhon setup.py sdist</code> （在 <code>django-polls``目录内）。这将创建一个名为 ``dist</code> 的目录并构建你自己的应用包， <code>django-polls-0.1.tar.gz</code>。</li>
</ul>
<figure class="highlight taggerscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">C:<span class="symbol">\U</span>sers<span class="symbol">\A</span>dministrator<span class="symbol">\D</span>esktop<span class="symbol">\d</span>jango-polls&gt; python setup.py sdist</span><br></pre></td></tr></table></figure>
<p>（2）使用自己的包名</p>
<p>由于我们把 <code>polls</code> 目录移出了项目，所以它无法工作了。我们现在要通过安装我们的新 <code>django-polls</code> 应用来修复这个问题。</p>
<figure class="highlight taggerscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">C:<span class="symbol">\U</span>sers<span class="symbol">\A</span>dministrator<span class="symbol">\D</span>esktop<span class="symbol">\d</span>jango-polls<span class="symbol">\d</span>ist&gt;pip install --user django-polls-0.1.tar.gz</span><br></pre></td></tr></table></figure>
<p><img src="https://github.com/bainingchao/imgurl/blob/master/dockerimg/1560310208431.png?raw=true" alt="1560310208431"></p>
<p>（3）发布应用，可以邮件，github等等方式上传</p>
<p>（4）模型</p>
<ul>
<li>字段类型详解：<a href="&lt;https://docs.djangoproject.com/zh-hans/2.2/ref/models/fields/#model-field-types">&lt;https://docs.djangoproject.com/zh-hans/2.2/ref/models/fields/#model-field-types</a>&gt;</li>
</ul>
<p>（5）查询</p>
<figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;</span>&gt; from blog.models import Blog</span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; b = Blog(name=<span class="string">'Beatles Blog'</span>, tagline=<span class="string">'All the latest Beatles news.'</span>)</span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; b.save()</span><br><span class="line"></span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; from blog.models import Author</span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; joe = Author.objects.create(name=<span class="string">"Joe"</span>)</span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; entry.authors.add(joe)</span><br><span class="line"></span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; all_entries = Entry.objects.all()</span><br><span class="line"></span><br><span class="line">Entry.objects.filter(pub_date__year=<span class="number">2006</span>)</span><br><span class="line">Entry.objects.all().filter(pub_date__year=<span class="number">2006</span>)</span><br><span class="line"></span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; q1 = Entry.objects.filter(headline__startswith=<span class="string">"What"</span>)</span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; q2 = q1.exclude(pub_date__gte=datetime.date.today())</span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; q3 = q1.filter(pub_date__gte=datetime.date.today())</span><br><span class="line"></span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; one_entry = Entry.objects.get(pk=<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; Entry.objects.all()[<span class="number">5</span><span class="symbol">:</span><span class="number">10</span>] <span class="comment"># OFFSET 5 LIMIT 5</span></span><br><span class="line"></span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; Entry.objects.order_by(<span class="string">'headline'</span>)[<span class="number">0</span>]</span><br><span class="line"></span><br><span class="line">SELECT * FROM blog_entry WHERE pub_date &lt;= <span class="string">'2006-01-01'</span>;</span><br><span class="line"></span><br><span class="line">Entry.objects.get(headline__exact=<span class="string">"Cat bites dog"</span>)</span><br><span class="line"></span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; Blog.objects.get(name__iexact=<span class="string">"beatles blog"</span>)</span><br><span class="line"></span><br><span class="line">Entry.objects.get(headline__contains=<span class="string">'Lennon'</span>)</span><br><span class="line"></span><br><span class="line">SELECT ... WHERE headline LIKE <span class="string">'%Lennon%'</span>;</span><br><span class="line"></span><br><span class="line">Blog.objects.filter(entry__authors__isnull=False, entry__authors__name__isnull=True)</span><br><span class="line"></span><br><span class="line">Blog.objects.filter(entry__headline__contains=<span class="string">'Lennon'</span>, entry__pub_date__year=<span class="number">2008</span>)</span><br><span class="line"></span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; Entry.objects.filter(mod_date__gt=F(<span class="string">'pub_date'</span>) + timedelta(days=<span class="number">3</span>))</span><br><span class="line"></span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; print([e.headline for e in Entry.objects.all()])</span><br><span class="line"></span><br><span class="line">SELECT * from polls WHERE question LIKE <span class="string">'Who%'</span></span><br><span class="line">    AND (pub_date = <span class="string">'2005-05-02'</span> OR pub_date = <span class="string">'2005-05-06'</span>)</span><br><span class="line"></span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; e.delete()</span><br><span class="line"></span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; Entry.objects.filter(pub_date__year=<span class="number">2005</span>).delete()</span><br><span class="line"></span><br><span class="line">e = Entry.objects.get(id=<span class="number">3</span>)</span><br><span class="line">e.authors.all() <span class="comment"># Returns all Author objects for this Entry.</span></span><br><span class="line">e.authors.count()</span><br><span class="line">e.authors.filter(name__contains=<span class="string">'John'</span>)</span><br><span class="line"></span><br><span class="line">a = Author.objects.get(id=<span class="number">5</span>)</span><br><span class="line">a.entry_set.all() <span class="comment"># Returns all Entry objects for this Author.</span></span><br><span class="line"></span><br><span class="line">e = Entry.objects.get(id=<span class="number">2</span>)</span><br><span class="line">e.entrydetail <span class="comment"># returns the related EntryDetail object</span></span><br><span class="line">e.entrydetail = ed</span><br></pre></td></tr></table></figure>
<p>（5）聚合</p>
<figure class="highlight haskell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="title">from</span> django.db <span class="keyword">import</span> models</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="type">Author</span>(<span class="title">models</span>.<span class="type">Model</span>):</span></span><br><span class="line"><span class="class">    name = models.<span class="type">CharField</span>(<span class="title">max_length</span>=100)</span></span><br><span class="line"><span class="class">    age = models.<span class="type">IntegerField</span>()</span></span><br><span class="line"><span class="class"></span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="type">Publisher</span>(<span class="title">models</span>.<span class="type">Model</span>):</span></span><br><span class="line"><span class="class">    name = models.<span class="type">CharField</span>(<span class="title">max_length</span>=300)</span></span><br><span class="line"><span class="class"></span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="type">Book</span>(<span class="title">models</span>.<span class="type">Model</span>):</span></span><br><span class="line"><span class="class">    name = models.<span class="type">CharField</span>(<span class="title">max_length</span>=300)</span></span><br><span class="line"><span class="class">    pages = models.<span class="type">IntegerField</span>()</span></span><br><span class="line"><span class="class">    price = models.<span class="type">DecimalField</span>(<span class="title">max_digits</span>=10, <span class="title">decimal_places</span>=2)</span></span><br><span class="line"><span class="class">    rating = models.<span class="type">FloatField</span>()</span></span><br><span class="line"><span class="class">    authors = models.<span class="type">ManyToManyField</span>(<span class="type">Author</span>)</span></span><br><span class="line"><span class="class">    publisher = models.<span class="type">ForeignKey</span>(<span class="type">Publisher</span>, <span class="title">on_delete</span>=<span class="title">models</span>.<span class="type">CASCADE</span>)</span></span><br><span class="line"><span class="class">    pubdate = models.<span class="type">DateField</span>()</span></span><br><span class="line"><span class="class"></span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="type">Store</span>(<span class="title">models</span>.<span class="type">Model</span>):</span></span><br><span class="line"><span class="class">    name = models.<span class="type">CharField</span>(<span class="title">max_length</span>=300)</span></span><br><span class="line"><span class="class">    books = models.<span class="type">ManyToManyField</span>(<span class="type">Book</span>)</span></span><br></pre></td></tr></table></figure>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> Total number of books.</span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash">&gt;&gt; Book.objects.count()</span></span><br><span class="line"></span><br><span class="line"><span class="meta">&gt;</span><span class="bash">&gt;&gt; Book.objects.filter(publisher__name=<span class="string">'BaloneyPress'</span>).count()</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> Average price across all books.</span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash">&gt;&gt; from django.db.models import Avg, Max, Min</span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash">&gt;&gt; Book.objects.aggregate(Avg(<span class="string">'price'</span>), Max(<span class="string">'price'</span>), Min(<span class="string">'price'</span>))</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> Each publisher, each with a count of books as a <span class="string">"num_books"</span> attribute.</span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash">&gt;&gt; pubs = Publisher.objects.annotate(num_books=Count(<span class="string">'book'</span>))</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> The top 5 publishers, <span class="keyword">in</span> order by number of books.</span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash">&gt;&gt; pubs = Publisher.objects.annotate(num_books=Count(<span class="string">'book'</span>)).order_by(<span class="string">'-num_books'</span>)[:5]</span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash">&gt;&gt; pubs[0].num_books</span></span><br><span class="line"></span><br><span class="line"><span class="meta">&gt;</span><span class="bash">&gt;&gt; Book.objects.filter(name__startswith=<span class="string">"Django"</span>).annotate(num_authors=Count(<span class="string">'authors'</span>))</span></span><br><span class="line"></span><br><span class="line"><span class="meta">&gt;</span><span class="bash">&gt;&gt; Book.objects.annotate(num_authors=Count(<span class="string">'authors'</span>)).filter(num_authors__gt=1)</span></span><br><span class="line"></span><br><span class="line"><span class="meta">&gt;</span><span class="bash">&gt;&gt; Book.objects.annotate(num_authors=Count(<span class="string">'authors'</span>)).order_by(<span class="string">'num_authors'</span>)</span></span><br></pre></td></tr></table></figure>
<p>（6）搜索</p>
<figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&gt;&gt;&gt; Entry<span class="selector-class">.objects</span><span class="selector-class">.filter</span>(body_text__search=<span class="string">'cheese'</span>)</span><br><span class="line"></span><br><span class="line">&gt;&gt;&gt; Entry<span class="selector-class">.objects</span><span class="selector-class">.annotate</span>(</span><br><span class="line">...     search=SearchVector(<span class="string">'blog__tagline'</span>, <span class="string">'body_text'</span>),</span><br><span class="line">... ).<span class="attribute">filter</span>(search=<span class="string">'cheese'</span>)</span><br></pre></td></tr></table></figure>
<p>（7）Manager</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.db <span class="keyword">import</span> models</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">PollManager</span><span class="params">(models.Manager)</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">with_counts</span><span class="params">(self)</span>:</span></span><br><span class="line">        <span class="keyword">from</span> django.db <span class="keyword">import</span> connection</span><br><span class="line">        <span class="keyword">with</span> connection.cursor() <span class="keyword">as</span> cursor:</span><br><span class="line">            cursor.execute(<span class="string">"""</span></span><br><span class="line"><span class="string">                SELECT p.id, p.question, p.poll_date, COUNT(*)</span></span><br><span class="line"><span class="string">                FROM polls_opinionpoll p, polls_response r</span></span><br><span class="line"><span class="string">                WHERE p.id = r.poll_id</span></span><br><span class="line"><span class="string">                GROUP BY p.id, p.question, p.poll_date</span></span><br><span class="line"><span class="string">                ORDER BY p.poll_date DESC"""</span>)</span><br><span class="line">            result_list = []</span><br><span class="line">            <span class="keyword">for</span> row <span class="keyword">in</span> cursor.fetchall():</span><br><span class="line">                p = self.model(id=row[<span class="number">0</span>], question=row[<span class="number">1</span>], poll_date=row[<span class="number">2</span>])</span><br><span class="line">                p.num_responses = row[<span class="number">3</span>]</span><br><span class="line">                result_list.append(p)</span><br><span class="line">        <span class="keyword">return</span> result_list</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">OpinionPoll</span><span class="params">(models.Model)</span>:</span></span><br><span class="line">    question = models.CharField(max_length=<span class="number">200</span>)</span><br><span class="line">    poll_date = models.DateField()</span><br><span class="line">    objects = PollManager()</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Response</span><span class="params">(models.Model)</span>:</span></span><br><span class="line">    poll = models.ForeignKey(OpinionPoll, on_delete=models.CASCADE)</span><br><span class="line">    person_name = models.CharField(max_length=<span class="number">50</span>)</span><br><span class="line">    response = models.TextField()</span><br></pre></td></tr></table></figure>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ExtraManager</span><span class="params">(models.Model)</span>:</span></span><br><span class="line">    extra_manager = OtherManager()</span><br><span class="line"></span><br><span class="line">    <span class="class"><span class="keyword">class</span> <span class="title">Meta</span>:</span></span><br><span class="line">        abstract = <span class="keyword">True</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ChildC</span><span class="params">(AbstractBase, ExtraManager)</span>:</span></span><br><span class="line">    <span class="comment"># ...</span></span><br><span class="line">    <span class="comment"># Default manager is CustomManager, but OtherManager is</span></span><br><span class="line">    <span class="comment"># also available via the "extra_manager" attribute.</span></span><br><span class="line">    <span class="keyword">pass</span></span><br></pre></td></tr></table></figure>
<p>（8）raw SQL queries</p>
<figure class="highlight haskell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="type">Person</span>(<span class="title">models</span>.<span class="type">Model</span>):</span></span><br><span class="line"><span class="class">    first_name = models.<span class="type">CharField</span>(...)</span></span><br><span class="line"><span class="class">    last_name = models.<span class="type">CharField</span>(...)</span></span><br><span class="line"><span class="class">    birth_date = models.<span class="type">DateField</span>(...)</span></span><br></pre></td></tr></table></figure>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">for</span> p <span class="keyword">in</span> Person.objects.raw(<span class="string">'SELECT * FROM myapp_person'</span>):</span><br><span class="line"><span class="meta">... </span>    print(p)</span><br><span class="line"></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>Person.objects.raw(<span class="string">'SELECT last_name, birth_date, first_name, id FROM myapp_person'</span>)</span><br><span class="line"></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>name_map = &#123;<span class="string">'first'</span>: <span class="string">'first_name'</span>, <span class="string">'last'</span>: <span class="string">'last_name'</span>, <span class="string">'bd'</span>: <span class="string">'birth_date'</span>, <span class="string">'pk'</span>: <span class="string">'id'</span>&#125;</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>Person.objects.raw(<span class="string">'SELECT * FROM some_other_table'</span>, translations=name_map)</span><br><span class="line"></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>first_person = Person.objects.raw(<span class="string">'SELECT * FROM myapp_person LIMIT 1'</span>)[<span class="number">0</span>]</span><br><span class="line"></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>people = Person.objects.raw(<span class="string">'SELECT *, age(birth_date) AS age FROM myapp_person'</span>)</span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">for</span> p <span class="keyword">in</span> people:</span><br><span class="line"><span class="meta">... </span>    print(<span class="string">"%s is %s."</span> % (p.first_name, p.age))</span><br><span class="line"></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>lname = <span class="string">'Doe'</span></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>Person.objects.raw(<span class="string">'SELECT * FROM myapp_person WHERE last_name = %s'</span>, [lname])</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> django.db <span class="keyword">import</span> connection</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">my_custom_sql</span><span class="params">(self)</span>:</span></span><br><span class="line">    <span class="keyword">with</span> connection.cursor() <span class="keyword">as</span> cursor:</span><br><span class="line">        cursor.execute(<span class="string">"UPDATE bar SET foo = 1 WHERE baz = %s"</span>, [self.baz])</span><br><span class="line">        cursor.execute(<span class="string">"SELECT foo FROM bar WHERE baz = %s"</span>, [self.baz])</span><br><span class="line">        row = cursor.fetchone()</span><br><span class="line">    <span class="keyword">return</span> row</span><br><span class="line"></span><br><span class="line">cursor.execute(<span class="string">"SELECT foo FROM bar WHERE baz = '30%%' AND id = %s"</span>, [self.id])</span><br><span class="line"></span><br><span class="line"><span class="meta">&gt;&gt;&gt; </span>cursor.fetchall()</span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> connection.cursor() <span class="keyword">as</span> c:</span><br><span class="line">    c.execute(...)</span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> connection.cursor() <span class="keyword">as</span> cursor:</span><br><span class="line">    cursor.callproc(<span class="string">'test_procedure'</span>, [<span class="number">1</span>, <span class="string">'test'</span>])</span><br></pre></td></tr></table></figure>
<p>(9) 数据事务</p>
<figure class="highlight applescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">a.save() <span class="comment"># Succeeds, but may be undone by transaction rollback</span></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    b.save() <span class="comment"># Could throw exception</span></span><br><span class="line">except IntegrityError:</span><br><span class="line">    <span class="keyword">transaction</span>.rollback()</span><br><span class="line">c.save() <span class="comment"># Succeeds, but a.save() may have been undone</span></span><br></pre></td></tr></table></figure>
<p>（10）多数据库</p>
<figure class="highlight xquery"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">DATABASES = &#123;</span><br><span class="line">    <span class="string">'default'</span>: &#123;</span><br><span class="line">        <span class="string">'NAME'</span>: <span class="string">'app_data'</span>,</span><br><span class="line">        <span class="string">'ENGINE'</span>: <span class="string">'django.db.backends.postgresql'</span>,</span><br><span class="line">        <span class="string">'USER'</span>: <span class="string">'postgres_user'</span>,</span><br><span class="line">        <span class="string">'PASSWORD'</span>: <span class="string">'s3krit'</span></span><br><span class="line">    &#125;,</span><br><span class="line">    <span class="string">'users'</span>: &#123;</span><br><span class="line">        <span class="string">'NAME'</span>: <span class="string">'user_data'</span>,</span><br><span class="line">        <span class="string">'ENGINE'</span>: <span class="string">'django.db.backends.mysql'</span>,</span><br><span class="line">        <span class="string">'USER'</span>: <span class="string">'mysql_user'</span>,</span><br><span class="line">        <span class="string">'PASSWORD'</span>: <span class="string">'priv4te'</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<figure class="highlight jboss-cli"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ <span class="string">./manage.py</span> migrate <span class="params">--database=users</span></span><br><span class="line">$ <span class="string">./manage.py</span> migrate <span class="params">--database=users</span></span><br><span class="line">$ <span class="string">./manage.py</span> migrate <span class="params">--database=customers</span></span><br></pre></td></tr></table></figure>
<figure class="highlight haskell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="title">from</span> django.contrib <span class="keyword">import</span> admin</span><br><span class="line"></span><br><span class="line"><span class="meta"># Specialize the multi-db admin objects for use with specific models.</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="type">BookInline</span>(<span class="type">MultiDBTabularInline</span>):</span></span><br><span class="line"><span class="class">    model = <span class="type">Book</span></span></span><br><span class="line"><span class="class"></span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="type">PublisherAdmin</span>(<span class="type">MultiDBModelAdmin</span>):</span></span><br><span class="line"><span class="class">    inlines = [<span class="type">BookInline</span>]</span></span><br><span class="line"><span class="class"></span></span><br><span class="line"><span class="class">admin.site.register(<span class="type">Author</span>, <span class="type">MultiDBModelAdmin</span>)</span></span><br><span class="line"><span class="class">admin.site.register(<span class="type">Publisher</span>, <span class="type">PublisherAdmin</span>)</span></span><br><span class="line"><span class="class"></span></span><br><span class="line"><span class="class">othersite = admin.<span class="type">AdminSite</span>('<span class="title">othersite'</span>)</span></span><br><span class="line"><span class="class">othersite.register(<span class="type">Publisher</span>, <span class="type">MultiDBModelAdmin</span>)</span></span><br></pre></td></tr></table></figure>
<figure class="highlight clean"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.db <span class="keyword">import</span> connections</span><br><span class="line"><span class="keyword">with</span> connections[<span class="string">'my_db_alias'</span>].cursor() <span class="keyword">as</span> cursor:</span><br><span class="line">    ...</span><br></pre></td></tr></table></figure>
<p>(11) URL调度器</p>
<figure class="highlight xl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">from django.urls <span class="keyword">import</span> <span class="built_in">path</span></span><br><span class="line">from . <span class="keyword">import</span> views</span><br><span class="line"></span><br><span class="line">urlpatterns = [</span><br><span class="line">    <span class="built_in">path</span>(<span class="string">'articles/2003/'</span>, views.special_case_2003),</span><br><span class="line">    <span class="built_in">path</span>(<span class="string">'articles/&lt;int:year&gt;/'</span>, views.year_archive),</span><br><span class="line">    <span class="built_in">path</span>(<span class="string">'articles/&lt;int:year&gt;/&lt;int:month&gt;/'</span>, views.month_archive),</span><br><span class="line">    <span class="built_in">path</span>(<span class="string">'articles/&lt;int:year&gt;/&lt;int:month&gt;/&lt;slug:slug&gt;/'</span>, views.article_detail),</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.urls <span class="keyword">import</span> path, re_path</span><br><span class="line"><span class="keyword">from</span> . <span class="keyword">import</span> views</span><br><span class="line"></span><br><span class="line">urlpatterns = [</span><br><span class="line">    path(<span class="string">'articles/2003/'</span>, views.special_case_2003),</span><br><span class="line">    re_path(<span class="string">r'^articles/(?P&lt;year&gt;[0-9]&#123;4&#125;)/$'</span>, views.year_archive),</span><br><span class="line">    re_path(<span class="string">r'^articles/(?P&lt;year&gt;[0-9]&#123;4&#125;)/(?P&lt;month&gt;[0-9]&#123;2&#125;)/$'</span>, views.month_archive),</span><br><span class="line">    re_path(<span class="string">r'^articles/(?P&lt;year&gt;[0-9]&#123;4&#125;)/(?P&lt;month&gt;[0-9]&#123;2&#125;)/(?P&lt;slug&gt;[\w-]+)/$'</span>, views.article_detail),</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.urls <span class="keyword">import</span> re_path</span><br><span class="line"></span><br><span class="line">urlpatterns = [</span><br><span class="line">    re_path(<span class="string">r'^blog/(page-(\d+)/)?$'</span>, blog_articles),                  <span class="comment"># bad</span></span><br><span class="line">    re_path(<span class="string">r'^comments/(?:page-(?P&lt;page_number&gt;\d+)/)?$'</span>, comments),  <span class="comment"># good</span></span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<figure class="highlight clean"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.urls <span class="keyword">import</span> include, path</span><br><span class="line"></span><br><span class="line">urlpatterns = [</span><br><span class="line">    # ... snip ...</span><br><span class="line">    path(<span class="string">'community/'</span>, include(<span class="string">'aggregator.urls'</span>)),</span><br><span class="line">    path(<span class="string">'contact/'</span>, include(<span class="string">'contact.urls'</span>)),</span><br><span class="line">    # ... snip ...</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<figure class="highlight xl"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"># In settings/urls/main.py</span><br><span class="line">from django.urls <span class="keyword">import</span> include, <span class="built_in">path</span></span><br><span class="line"></span><br><span class="line">urlpatterns = [</span><br><span class="line">    <span class="built_in">path</span>(<span class="string">'&lt;username&gt;/blog/'</span>, include(<span class="string">'foo.urls.blog'</span>)),</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"># In foo/urls/blog.py</span><br><span class="line">from django.urls <span class="keyword">import</span> <span class="built_in">path</span></span><br><span class="line">from . <span class="keyword">import</span> views</span><br><span class="line"></span><br><span class="line">urlpatterns = [</span><br><span class="line">    <span class="built_in">path</span>(<span class="string">''</span>, views.blog.index),</span><br><span class="line">    <span class="built_in">path</span>(<span class="string">'archive/'</span>, views.blog.archive),</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<p>示例</p>
<figure class="highlight clean"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.urls <span class="keyword">import</span> path</span><br><span class="line"><span class="keyword">from</span> . <span class="keyword">import</span> views</span><br><span class="line"></span><br><span class="line">urlpatterns = [</span><br><span class="line">    #...</span><br><span class="line">    path(<span class="string">'articles/&lt;int:year&gt;/'</span>, views.year_archive, name=<span class="string">'news-year-archive'</span>),</span><br><span class="line">    #...</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<figure class="highlight django"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">"</span></span></span><span class="template-tag">&#123;% <span class="name"><span class="name">url</span></span> 'news-year-archive' 2012 %&#125;</span><span class="xml"><span class="tag"><span class="string">"</span>&gt;</span>2012 Archive<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span></span><br><span class="line"><span class="xml"></span><span class="comment">&#123;# Or with the year in a template context variable: #&#125;</span><span class="xml"></span></span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">ul</span>&gt;</span></span></span><br><span class="line"><span class="xml"></span><span class="template-tag">&#123;% <span class="name"><span class="name">for</span></span> yearvar <span class="keyword">in</span> year_list %&#125;</span><span class="xml"></span></span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">li</span>&gt;</span><span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">"</span></span></span><span class="template-tag">&#123;% <span class="name"><span class="name">url</span></span> 'news-year-archive' yearvar %&#125;</span><span class="xml"><span class="tag"><span class="string">"</span>&gt;</span></span><span class="template-variable">&#123;&#123; yearvar &#125;&#125;</span><span class="xml"> Archive<span class="tag">&lt;/<span class="name">a</span>&gt;</span><span class="tag">&lt;/<span class="name">li</span>&gt;</span></span></span><br><span class="line"><span class="xml"></span><span class="template-tag">&#123;% <span class="name"><span class="name">endfor</span></span> %&#125;</span><span class="xml"></span></span><br><span class="line"><span class="xml"><span class="tag">&lt;/<span class="name">ul</span>&gt;</span></span></span><br></pre></td></tr></table></figure>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.http <span class="keyword">import</span> HttpResponseRedirect</span><br><span class="line"><span class="keyword">from</span> django.urls <span class="keyword">import</span> reverse</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">redirect_to_year</span><span class="params">(request)</span>:</span></span><br><span class="line">    <span class="comment"># ...</span></span><br><span class="line">    year = <span class="number">2006</span></span><br><span class="line">    <span class="comment"># ...</span></span><br><span class="line">    <span class="keyword">return</span> HttpResponseRedirect(reverse(<span class="string">'news-year-archive'</span>, args=(year,)))</span><br></pre></td></tr></table></figure>
<p>（12）文件上传</p>
<p>forms.py</p>
<figure class="highlight haskell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="title">from</span> django <span class="keyword">import</span> forms</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="type">UploadFileForm</span>(<span class="title">forms</span>.<span class="type">Form</span>):</span></span><br><span class="line"><span class="class">    title = forms.<span class="type">CharField</span>(<span class="title">max_length</span>=50)</span></span><br><span class="line"><span class="class">    file = forms.<span class="type">FileField</span>()</span></span><br></pre></td></tr></table></figure>
<p>views.py</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.http <span class="keyword">import</span> HttpResponseRedirect</span><br><span class="line"><span class="keyword">from</span> django.shortcuts <span class="keyword">import</span> render</span><br><span class="line"><span class="keyword">from</span> .forms <span class="keyword">import</span> ModelFormWithFileField</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">upload_file</span><span class="params">(request)</span>:</span></span><br><span class="line">    <span class="keyword">if</span> request.method == <span class="string">'POST'</span>:</span><br><span class="line">        form = ModelFormWithFileField(request.POST, request.FILES)</span><br><span class="line">        <span class="keyword">if</span> form.is_valid():</span><br><span class="line">            <span class="comment"># file is saved</span></span><br><span class="line">            form.save()</span><br><span class="line">            <span class="keyword">return</span> HttpResponseRedirect(<span class="string">'/success/url/'</span>)</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        form = ModelFormWithFileField()</span><br><span class="line">    <span class="keyword">return</span> render(request, <span class="string">'upload.html'</span>, &#123;<span class="string">'form'</span>: form&#125;)</span><br></pre></td></tr></table></figure>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">handle_uploaded_file</span><span class="params">(f)</span>:</span></span><br><span class="line">    <span class="keyword">with</span> open(<span class="string">'some/file/name.txt'</span>, <span class="string">'wb+'</span>) <span class="keyword">as</span> destination:</span><br><span class="line">        <span class="keyword">for</span> chunk <span class="keyword">in</span> f.chunks():</span><br><span class="line">            destination.write(chunk)</span><br></pre></td></tr></table></figure>
<p>使用 <code>UploadedFile.chunks()</code> 而不是 <code>read()</code> 是为了确保即使是大文件又不会将我们系统的内存占满。</p>
<p>（13）快捷键函数</p>
<ul>
<li>render(request, template_name, <em>context=None**</em>,<strong> <em>content_type=None</em></strong>,<strong> <em>status=None</em></strong>,** using=None)</li>
</ul>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.shortcuts <span class="keyword">import</span> render</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">my_view</span><span class="params">(request)</span>:</span></span><br><span class="line">    <span class="comment"># View code here...</span></span><br><span class="line">    <span class="keyword">return</span> render(request, <span class="string">'myapp/index.html'</span>, &#123;</span><br><span class="line">        <span class="string">'foo'</span>: <span class="string">'bar'</span>,</span><br><span class="line">    &#125;, content_type=<span class="string">'application/xhtml+xml'</span>)</span><br></pre></td></tr></table></figure>
<ul>
<li><code>redirect</code><strong>(</strong>to<strong>,</strong> <strong>args</strong>, <em>permanent=False**</em>,<strong> *</strong>kwargs<em>*</em>)</li>
</ul>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.shortcuts <span class="keyword">import</span> redirect</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">my_view</span><span class="params">(request)</span>:</span></span><br><span class="line">    ...</span><br><span class="line">    obj = MyModel.objects.get(...)</span><br><span class="line">    <span class="keyword">return</span> redirect(obj)</span><br></pre></td></tr></table></figure>
<p>（14）中间件</p>
<p>中间件是 Django 请求/响应处理的钩子框架。它是一个轻量级的、低级的“插件”系统，用于全局改变 Django 的输入或输出。</p>
<p>中间件工厂是一个可调用的程序，它接受 <code>get_response</code> 可调用并返回中间件。中间件是可调用的，它接受请求并返回响应，就像视图一样。</p>
<figure class="highlight vbscript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">def simple_middleware(get_response):</span><br><span class="line">    # One-<span class="built_in">time</span> configuration <span class="keyword">and</span> initialization.</span><br><span class="line"></span><br><span class="line">    def middleware(<span class="built_in">request</span>):</span><br><span class="line">        # Code <span class="keyword">to</span> be executed <span class="keyword">for</span> <span class="keyword">each</span> <span class="built_in">request</span> before</span><br><span class="line">        # the view (<span class="keyword">and</span> later middleware) are called.</span><br><span class="line"></span><br><span class="line">        <span class="built_in">response</span> = get_response(<span class="built_in">request</span>)</span><br><span class="line"></span><br><span class="line">        # Code <span class="keyword">to</span> be executed <span class="keyword">for</span> <span class="keyword">each</span> <span class="built_in">request</span>/<span class="built_in">response</span> after</span><br><span class="line">        # the view <span class="keyword">is</span> called.</span><br><span class="line"></span><br><span class="line">        return <span class="built_in">response</span></span><br><span class="line"></span><br><span class="line">    return middleware</span><br></pre></td></tr></table></figure>
<p>或者它可以写成一个类，它的实例是可调用的，如下：</p>
<figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">SimpleMiddleware</span>:</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span><span class="params">(<span class="keyword">self</span>, get_response)</span></span><span class="symbol">:</span></span><br><span class="line">        <span class="keyword">self</span>.get_response = get_response</span><br><span class="line">        <span class="comment"># One-time configuration and initialization.</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__call__</span><span class="params">(<span class="keyword">self</span>, request)</span></span><span class="symbol">:</span></span><br><span class="line">        <span class="comment"># Code to be executed for each request before</span></span><br><span class="line">        <span class="comment"># the view (and later middleware) are called.</span></span><br><span class="line"></span><br><span class="line">        response = <span class="keyword">self</span>.get_response(request)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># Code to be executed for each request/response after</span></span><br><span class="line">        <span class="comment"># the view is called.</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> response</span><br></pre></td></tr></table></figure>
<p>中间件工厂必须接受 <code>get_response</code> 参数。还可以初始化中间件的一些全局状态。记住两个注意事项：</p>
<ul>
<li>Django仅用 <code>get_response</code> 参数初始化您的中间件，因此不能定义 <code>__init__()</code>，因为需要其他参数。</li>
<li>与每次请求调用 <code>__call__()</code> 方法不同，当 Web 服务器启动时，<code>__init__()</code> 只被称为<em>一次</em>。</li>
</ul>
<font style="color:red">激活中间件</font>

<p>若要激活中间件组件，请将其添加到 Django 设置中的 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/settings/#std:setting-MIDDLEWARE" target="_blank" rel="noopener"><code>MIDDLEWARE</code></a> 列表中。在 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/settings/#std:setting-MIDDLEWARE" target="_blank" rel="noopener"><code>MIDDLEWARE</code></a> 中，每个中间件组件由字符串表示：指向中间件工厂的类或函数名的完整 Python 路径。例如，这里创建的默认值是 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/django-admin/#django-admin-startproject" target="_blank" rel="noopener"><code>django-admin startproject</code></a>：</p>
<figure class="highlight ebnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="attribute">MIDDLEWARE</span> = [</span><br><span class="line">    <span class="string">'django.middleware.security.SecurityMiddleware'</span>,</span><br><span class="line">    <span class="string">'django.contrib.sessions.middleware.SessionMiddleware'</span>,</span><br><span class="line">    <span class="string">'django.middleware.common.CommonMiddleware'</span>,</span><br><span class="line">    <span class="string">'django.middleware.csrf.CsrfViewMiddleware'</span>,</span><br><span class="line">    <span class="string">'django.contrib.auth.middleware.AuthenticationMiddleware'</span>,</span><br><span class="line">    <span class="string">'django.contrib.messages.middleware.MessageMiddleware'</span>,</span><br><span class="line">    <span class="string">'django.middleware.clickjacking.XFrameOptionsMiddleware'</span>,</span><br><span class="line">]</span><br></pre></td></tr></table></figure>
<p><a href="https://docs.djangoproject.com/zh-hans/2.2/ref/settings/#std:setting-MIDDLEWARE" target="_blank" rel="noopener"><code>MIDDLEWARE</code></a> 的顺序很重要，因为中间件会依赖其他中间件。例如：类 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/middleware/#django.contrib.auth.middleware.AuthenticationMiddleware" target="_blank" rel="noopener"><code>AuthenticationMiddleware</code></a> 在会话中存储经过身份验证的用户；因此，它必须在 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/middleware/#django.contrib.sessions.middleware.SessionMiddleware" target="_blank" rel="noopener"><code>SessionMiddleware</code></a> 后面运行 。中间件。Session中间件。请参阅 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/middleware/#middleware-ordering" target="_blank" rel="noopener">Middleware ordering</a> ，用于一些关于 Django 中间件类排序的常见提示。</p>
<p>(15) 会话</p>
<figure class="highlight haxe"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">def post_comment(request, <span class="keyword">new</span><span class="type">_comment</span>):<span class="type"></span></span><br><span class="line"><span class="type">    if request</span>.session.<span class="keyword">get</span>(<span class="string">'has_commented'</span>, False):<span class="type"></span></span><br><span class="line"><span class="type">        return HttpResponse</span>(<span class="string">"You've already commented."</span>)</span><br><span class="line">    c = comments.Comment(comment=<span class="keyword">new</span><span class="type">_comment</span>)</span><br><span class="line">    c.save()</span><br><span class="line">    request.session[<span class="string">'has_commented'</span>] = True</span><br><span class="line">    <span class="keyword">return</span> HttpResponse(<span class="string">'Thanks for your comment!'</span>)</span><br></pre></td></tr></table></figure>
<p>This simplistic view logs in a “member” of the site:</p>
<figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">def login(request):</span><br><span class="line">    m = Member.objects.<span class="keyword">get</span>(username=request.POST[<span class="string">'username'</span>])</span><br><span class="line">    <span class="keyword">if</span> m.password == request.POST[<span class="string">'password'</span>]:</span><br><span class="line">        request.session[<span class="string">'member_id'</span>] = m.id</span><br><span class="line">        <span class="keyword">return</span> HttpResponse(<span class="string">"You're logged in."</span>)</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="keyword">return</span> HttpResponse(<span class="string">"Your username and password didn't match."</span>)</span><br></pre></td></tr></table></figure>
<p>…And this one logs a member out, according to <code>login()</code> above:</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">logout</span><span class="params">(request)</span>:</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="keyword">del</span> request.session[<span class="string">'member_id'</span>]</span><br><span class="line">    <span class="keyword">except</span> KeyError:</span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line">    <span class="keyword">return</span> HttpResponse(<span class="string">"You're logged out."</span>)</span><br></pre></td></tr></table></figure>
<p>（16）表单</p>
<p>16.1 Get与POST</p>
<p>处理表单时只会用到 <code>GET</code> 和 <code>POST</code> 两种HTTP方法。Django的登录表单使用 <code>POST</code> 方法传输数据，在这个方法中浏览器会封装表单数据，为了传输会进行编码，然后发送到服务端并接收它的响应。相比之下，<code>GET</code> 方法将提交的数据捆绑到一个字符串中，并用它来组成一个URL。该URL包含了数据要发送的地址以及一些键值对应的数据。如果您在Django文档中进行一次搜索，就会看到这点，它会生成一个形似 <code>https://docs.djangoproject.com/search/?q=forms&amp;release=1</code> 的URL。</p>
<p><code>GET</code> 和 <code>POST</code> 通常用于不同的目的。</p>
<p>任何可能用于更改系统状态的请求应该使用 <code>POST</code> —— 比如一个更改数据库的请求。<code>GET</code> 应该只被用于不会影响系统状态的请求。</p>
<p><code>GET</code> 方法也不适合密码表单，因为密码会出现在URL中，于是也会出现在浏览器的历史记录以及服务器的日志中，而且都是以纯文本的形式。它也不适合处理大量的数据或者二进制数据，比如一张图片。在WEB应用的管理表单中使用 <code>GET</code> 请求具有安全隐患：攻击者很容易通过模拟请求来访问系统的敏感数据。 <code>POST</code> 方法通过与其他像Django的 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/csrf/" target="_blank" rel="noopener">CSRF protection</a> 这样的保护措施配合使用，能对访问提供更多的控制。另一方面， <code>GET</code> 方法适用于诸如网页搜索表单这样的内容，因为这类呈现为一个 <code>GET</code>请求的URL很容易被存为书签、分享或重新提交。</p>
<figure class="highlight stata"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">&lt;<span class="keyword">form</span> action=<span class="string">"/your-name/"</span> method=<span class="string">"post"</span>&gt;</span><br><span class="line">    &lt;<span class="keyword">label</span> <span class="keyword">for</span>=<span class="string">"your_name"</span>&gt;Your name: &lt;/<span class="keyword">label</span>&gt;</span><br><span class="line">    &lt;<span class="keyword">input</span> id=<span class="string">"your_name"</span> <span class="keyword">type</span>=<span class="string">"text"</span> name=<span class="string">"your_name"</span> value=<span class="string">"&#123;&#123; current_name &#125;&#125;"</span>&gt;</span><br><span class="line">    &lt;<span class="keyword">input</span> <span class="keyword">type</span>=<span class="string">"submit"</span> value=<span class="string">"OK"</span>&gt;</span><br><span class="line">&lt;/<span class="keyword">form</span>&gt;</span><br></pre></td></tr></table></figure>
<p>forms.py</p>
<figure class="highlight haskell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="title">from</span> django <span class="keyword">import</span> forms</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="type">NameForm</span>(<span class="title">forms</span>.<span class="type">Form</span>):</span></span><br><span class="line"><span class="class">    your_name = forms.<span class="type">CharField</span>(<span class="title">label</span>='<span class="type">Your</span> <span class="title">name'</span>, <span class="title">max_length</span>=100)</span></span><br></pre></td></tr></table></figure>
<p>views.py</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.http <span class="keyword">import</span> HttpResponseRedirect</span><br><span class="line"><span class="keyword">from</span> django.shortcuts <span class="keyword">import</span> render</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> .forms <span class="keyword">import</span> NameForm</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">get_name</span><span class="params">(request)</span>:</span></span><br><span class="line">    <span class="comment"># if this is a POST request we need to process the form data</span></span><br><span class="line">    <span class="keyword">if</span> request.method == <span class="string">'POST'</span>:</span><br><span class="line">        <span class="comment"># create a form instance and populate it with data from the request:</span></span><br><span class="line">        form = NameForm(request.POST)</span><br><span class="line">        <span class="comment"># check whether it's valid:</span></span><br><span class="line">        <span class="keyword">if</span> form.is_valid():</span><br><span class="line">            <span class="comment"># process the data in form.cleaned_data as required</span></span><br><span class="line">            <span class="comment"># ...</span></span><br><span class="line">            <span class="comment"># redirect to a new URL:</span></span><br><span class="line">            <span class="keyword">return</span> HttpResponseRedirect(<span class="string">'/thanks/'</span>)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># if a GET (or any other method) we'll create a blank form</span></span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        form = NameForm()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> render(request, <span class="string">'name.html'</span>, &#123;<span class="string">'form'</span>: form&#125;)</span><br></pre></td></tr></table></figure>
<p>视图将再次创建一个表单实例并使用请求中的数据填充它： <code>form = NameForm(request.POST)</code> 这叫“绑定数据到表单” （现在它是一张 <em>绑定的</em> 表单）。调用表单的 <code>is_valid()</code> 方法；如果不为 <code>True</code> ，我们带着表单返回到模板。这次表单不再为空（ <em>未绑定</em> ），所以HTML表单将用之前提交的数据进行填充，放到可以根据需要进行编辑和修正的位置。如果 <code>is_valid()</code> 为 <code>True</code> ，我们就能在其 <code>cleaned_data</code> 属性中找到所有通过验证的表单数据。我们可以在发送一个HTTP重定向告诉浏览器下一步去向之前用这些数据更新数据库或者做其他处理。</p>
<p><strong>name.html</strong></p>
<figure class="highlight django"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">form</span> <span class="attr">action</span>=<span class="string">"/your-name/"</span> <span class="attr">method</span>=<span class="string">"post"</span>&gt;</span></span></span><br><span class="line"><span class="xml">    </span><span class="template-tag">&#123;% <span class="name"><span class="name">csrf_token</span></span> %&#125;</span><span class="xml"></span></span><br><span class="line"><span class="xml">    </span><span class="template-variable">&#123;&#123; form &#125;&#125;</span><span class="xml"></span></span><br><span class="line"><span class="xml">    <span class="tag">&lt;<span class="name">input</span> <span class="attr">type</span>=<span class="string">"submit"</span> <span class="attr">value</span>=<span class="string">"Submit"</span>&gt;</span></span></span><br><span class="line"><span class="xml"><span class="tag">&lt;/<span class="name">form</span>&gt;</span></span></span><br></pre></td></tr></table></figure>
<p>16.2 详解Django Form类</p>
<p>forms.py</p>
<figure class="highlight haskell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="title">from</span> django <span class="keyword">import</span> forms</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="type">ContactForm</span>(<span class="title">forms</span>.<span class="type">Form</span>):</span></span><br><span class="line"><span class="class">    subject = forms.<span class="type">CharField</span>(<span class="title">max_length</span>=100)</span></span><br><span class="line"><span class="class">    message = forms.<span class="type">CharField</span>(<span class="title">widget</span>=<span class="title">forms</span>.<span class="type">Textarea</span>)</span></span><br><span class="line"><span class="class">    sender = forms.<span class="type">EmailField</span>()</span></span><br><span class="line"><span class="class">    cc_myself = forms.<span class="type">BooleanField</span>(<span class="title">required</span>=<span class="type">False</span>)</span></span><br></pre></td></tr></table></figure>
<p>views.py</p>
<figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">from django<span class="selector-class">.core</span><span class="selector-class">.mail</span> import send_mail</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> <span class="selector-tag">form</span>.is_valid():</span><br><span class="line">    subject = <span class="selector-tag">form</span><span class="selector-class">.cleaned_data</span>[<span class="string">'subject'</span>]</span><br><span class="line">    message = <span class="selector-tag">form</span><span class="selector-class">.cleaned_data</span>[<span class="string">'message'</span>]</span><br><span class="line">    sender = <span class="selector-tag">form</span><span class="selector-class">.cleaned_data</span>[<span class="string">'sender'</span>]</span><br><span class="line">    cc_myself = <span class="selector-tag">form</span><span class="selector-class">.cleaned_data</span>[<span class="string">'cc_myself'</span>]</span><br><span class="line"></span><br><span class="line">    recipients = [<span class="string">'info@example.com'</span>]</span><br><span class="line">    <span class="keyword">if</span> cc_myself:</span><br><span class="line">        recipients.append(sender)</span><br><span class="line"></span><br><span class="line">    send_mail(subject, message, sender, recipients)</span><br><span class="line">    return HttpResponseRedirect(<span class="string">'/thanks/'</span>)</span><br></pre></td></tr></table></figure>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">&lt;p&gt;<span class="xml"><span class="tag">&lt;<span class="name">label</span> <span class="attr">for</span>=<span class="string">"id_subject"</span>&gt;</span>Subject:<span class="tag">&lt;/<span class="name">label</span>&gt;</span></span></span><br><span class="line">    &lt;input id=<span class="string">"id_subject"</span> type=<span class="string">"text"</span> name=<span class="string">"subject"</span> maxlength=<span class="string">"100"</span> required&gt;<span class="xml"><span class="tag">&lt;/<span class="name">p</span>&gt;</span></span></span><br><span class="line">&lt;p&gt;<span class="xml"><span class="tag">&lt;<span class="name">label</span> <span class="attr">for</span>=<span class="string">"id_message"</span>&gt;</span>Message:<span class="tag">&lt;/<span class="name">label</span>&gt;</span></span></span><br><span class="line">    &lt;textarea name=<span class="string">"message"</span> id=<span class="string">"id_message"</span> required&gt;<span class="xml"><span class="tag">&lt;/<span class="name">textarea</span>&gt;</span></span><span class="xml"><span class="tag">&lt;/<span class="name">p</span>&gt;</span></span></span><br><span class="line">&lt;p&gt;<span class="xml"><span class="tag">&lt;<span class="name">label</span> <span class="attr">for</span>=<span class="string">"id_sender"</span>&gt;</span>Sender:<span class="tag">&lt;/<span class="name">label</span>&gt;</span></span></span><br><span class="line">    &lt;input type=<span class="string">"email"</span> name=<span class="string">"sender"</span> id=<span class="string">"id_sender"</span> required&gt;<span class="xml"><span class="tag">&lt;/<span class="name">p</span>&gt;</span></span></span><br><span class="line">&lt;p&gt;<span class="xml"><span class="tag">&lt;<span class="name">label</span> <span class="attr">for</span>=<span class="string">"id_cc_myself"</span>&gt;</span>Cc myself:<span class="tag">&lt;/<span class="name">label</span>&gt;</span></span></span><br><span class="line">    &lt;input type=<span class="string">"checkbox"</span> name=<span class="string">"cc_myself"</span> id=<span class="string">"id_cc_myself"</span>&gt;<span class="xml"><span class="tag">&lt;/<span class="name">p</span>&gt;</span></span></span><br></pre></td></tr></table></figure>
<p>（17）用户认证</p>
<p>Django 自带一个用户验证系统。它负责处理用户账号、组、权限和基于cookie的用户会话。文档的这部分解释了默认的实现如何开箱即用，以及如何扩展和自定义以满足你的项目需求。</p>
<p>（18）验证系统</p>
<p>创建用户</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;</span><span class="bash">&gt;&gt; from django.contrib.auth.models import User</span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash">&gt;&gt; user = User.objects.create_user(<span class="string">'john'</span>, <span class="string">'lennon@thebeatles.com'</span>, <span class="string">'johnpassword'</span>)</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> At this point, user is a User object that has already been saved</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> to the database. You can <span class="built_in">continue</span> to change its attributes</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> <span class="keyword">if</span> you want to change other fields.</span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash">&gt;&gt; user.last_name = <span class="string">'Lennon'</span></span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash">&gt;&gt; user.save()</span></span><br></pre></td></tr></table></figure>
<p>创建超级用户</p>
<figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ python manage.py createsuperuser <span class="attribute">--username</span>=joe <span class="attribute">--email</span>=joe@example.com</span><br></pre></td></tr></table></figure>
<p>更改密码</p>
<figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;</span>&gt; from django.contrib.auth.models import User</span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; u = User.objects.get(username=<span class="string">'john'</span>)</span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; u.set_password(<span class="string">'new password'</span>)</span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; u.save()</span><br></pre></td></tr></table></figure>
<p>验证用户</p>
<figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.contrib.auth import authenticate</span><br><span class="line">user = authenticate(<span class="attribute">username</span>=<span class="string">'john'</span>, <span class="attribute">password</span>=<span class="string">'secret'</span>)</span><br><span class="line"><span class="keyword">if</span><span class="built_in"> user </span>is <span class="keyword">not</span> None:</span><br><span class="line">    # A backend authenticated the credentials</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    # <span class="literal">No</span> backend authenticated the credentials</span><br></pre></td></tr></table></figure>
<p>登出</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.contrib.auth <span class="keyword">import</span> logout</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">logout_view</span><span class="params">(request)</span>:</span></span><br><span class="line">    logout(request)</span><br><span class="line">    <span class="comment"># Redirect to a success page.</span></span><br></pre></td></tr></table></figure>
<p>限制对未登录用户的访问：装饰器</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.contrib.auth.decorators <span class="keyword">import</span> login_required</span><br><span class="line"></span><br><span class="line"><span class="meta">@login_required</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">my_view</span><span class="params">(request)</span>:</span></span><br><span class="line">    ...</span><br></pre></td></tr></table></figure>
<p>（19）Django缓存框架</p>
<p>动态网站存在一个基本权衡是——它们是动态的。每次用户请求一个页面，web 服务器需要提供各种各样的计算——从数据库查询到模板渲染再到业务逻辑——最后建立页面呈现给用户。从处理开销的角度来看，这比标准读取文件系统服务安排的开销要高得多。下面是一些伪代码解释了动态网站生成页面的时候，缓存是怎么工作的：</p>
<figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">given a URL, try finding that<span class="built_in"> page </span><span class="keyword">in</span> the cache</span><br><span class="line"><span class="keyword">if</span> the<span class="built_in"> page </span>is <span class="keyword">in</span> the cache:</span><br><span class="line">    return the cached page</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">    generate the page</span><br><span class="line">    save the generated<span class="built_in"> page </span><span class="keyword">in</span> the cache (<span class="keyword">for</span> next time)</span><br><span class="line">    return the generated page</span><br></pre></td></tr></table></figure>
<p>19.1 Memcached内存缓存</p>
<p>Memcached 是一个完全基于内存的缓存服务器，是 Django 原生支持的最快、最高效的缓存类型，最初被开发出来用于处理 LiveJournal.com 的高负载，随后由 Danga Interactive 开源。Facebook 和 Wikipedia 等网站使用它来减少数据库访问并显著提高网站性能。</p>
<p>Memcached 以一个守护进程的形式运行，并且被分配了指定数量的 RAM。它所做的就是提供一个快速接口用于在缓存中添加，检索和删除数据。所有数据都直接存储在内存中，因此不会产生数据库或文件系统使用的开销。</p>
<p>在安装 Memcached 本身后，你还需要安装一个 Memcached 绑定。有许多可用的 Python Memcached 绑定，最常见的两个是 python-memcached 和pylibmc</p>
<p>在 Django 中使用 Memcached ：</p>
<ul>
<li>将 BACKEND<caches-backend> 设置为 django.core.cache.backends.memcached.MemcachedCache 或者 django.core.cache.backends.memcached.PyLibMCCache （取决于你所选择的 memcached 绑定）</caches-backend></li>
<li>将 LOCATION<caches-location>设置为 ip:port 值，其中 ip 是 Memcached 守护进程的 IP 地址，port 是运行 Memcached 的端口；或者设置为一个 unix:path 值，其中 path 是 Memcached Unix 套接字文件的路径。</caches-location></li>
</ul>
<p>在这个示例中，Memcached 使用 python-memcached 绑定，在 localhost (127.0.0.1) 端口 11211 上运行：</p>
<figure class="highlight xquery"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">CACHES = &#123;</span><br><span class="line">    <span class="string">'default'</span>: &#123;</span><br><span class="line">        <span class="string">'BACKEND'</span>: <span class="string">'django.core.cache.backends.memcached.MemcachedCache'</span>,</span><br><span class="line">        <span class="string">'LOCATION'</span>: <span class="string">'127.0.0.1:11211'</span>,</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>在这个示例中， Memcached 可通过本地 Unix 套接字文件 /tmp/memcached.sock 使用 python-memcached 绑定得到：</p>
<figure class="highlight roboconf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">CACHES = &#123;</span><br><span class="line">    '<span class="attribute">default'</span>: &#123;</span><br><span class="line">        'BACKEND': 'django<span class="variable">.core</span><span class="variable">.cache</span><span class="variable">.backends</span><span class="variable">.memcached</span><span class="variable">.MemcachedCache</span>',</span><br><span class="line">        'LOCATION': 'unix:/tmp/memcached<span class="variable">.sock</span>',</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>当使用 pylibmc 绑定时，不要包含 unix:/ 前缀：</p>
<figure class="highlight roboconf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">CACHES = &#123;</span><br><span class="line">    '<span class="attribute">default'</span>: &#123;</span><br><span class="line">        'BACKEND': 'django<span class="variable">.core</span><span class="variable">.cache</span><span class="variable">.backends</span><span class="variable">.memcached</span><span class="variable">.PyLibMCCache</span>',</span><br><span class="line">        'LOCATION': '/tmp/memcached<span class="variable">.sock</span>',</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>Memcached 的一个出色功能是它能够在多个服务器上共享缓存。这意味着您可以在多台计算机上运行 Memcached 守护程序，程序会视这组计算机为单个缓存，而无需在每台机器上复制缓存值。要使用此功能，需要在 LOCATION 中包含所有服务器的地址，可以是分号或者逗号分隔的字符串，也可以是一个列表。</p>
<p>在这个示例中，缓存通过端口 11211 的 IP 地址 172.19.26.240 、 172.19.26.242 运行的 Memcached 实例共享：</p>
<figure class="highlight 1c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">CACHES = &#123;</span><br><span class="line">    'default': &#123;</span><br><span class="line">        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',</span><br><span class="line">        'LOCATION': [</span><br><span class="line">            '172.19.26.240:<span class="number">1121</span>1',</span><br><span class="line">            '172.19.26.242:<span class="number">1121</span>1',</span><br><span class="line">        ]</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>在以下示例中，缓存通过在 IP 地址 172.19.26.240（端口号 11211），172.19.26.242（端口号 11212）和 172.19.26.244（端口号 11213）上运行的 Memcached 实例共享：</p>
<figure class="highlight 1c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">CACHES = &#123;</span><br><span class="line">    'default': &#123;</span><br><span class="line">        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',</span><br><span class="line">        'LOCATION': [</span><br><span class="line">            '172.19.26.240:<span class="number">1121</span>1',</span><br><span class="line">            '172.19.26.242:<span class="number">1121</span>2',</span><br><span class="line">            '172.19.26.244:<span class="number">1121</span>3',</span><br><span class="line">        ]</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>关于 Memcached 的最后一点是，基于内存的缓存有一个缺点：因为缓存的数据存储在内存中，如果服务器崩溃，那么数据将会丢失。显然，内存不适用于持久数据存储，因此不要依赖基于内存的缓存作为你唯一的数据存储。毫无疑问，没有任何 Django 缓存后端应该被用于持久存储——它们都是适用于缓存的解决方案，而不是存储——我们在这里指出这一点是因为基于内存的缓存是格外临时的。</p>
<p>19.2 数据库缓存</p>
<p>Django 可以在数据库中存储缓存数据。如果你有一个快速、索引正常的数据库服务器，这种缓存效果最好。</p>
<p>用数据库表作为你的缓存后端：</p>
<ul>
<li>将 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/settings/#std:setting-CACHES-BACKEND" target="_blank" rel="noopener"><code>BACKEND</code></a> 设置为 <code>django.core.cache.backends.db.DatabaseCache</code></li>
<li>将 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/settings/#std:setting-CACHES-LOCATION" target="_blank" rel="noopener"><code>LOCATION</code></a> 设置为 数据库表的<code>表名</code>。这个表名可以是没有使用过的任何符合要求的名称。</li>
</ul>
<p>在这个例子中，缓存表的名称是 <code>my_cache_table</code> ：</p>
<figure class="highlight xquery"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">CACHES = &#123;</span><br><span class="line">    <span class="string">'default'</span>: &#123;</span><br><span class="line">        <span class="string">'BACKEND'</span>: <span class="string">'django.core.cache.backends.db.DatabaseCache'</span>,</span><br><span class="line">        <span class="string">'LOCATION'</span>: <span class="string">'my_cache_table'</span>,</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li>创建缓存表</li>
</ul>
<p>使用数据库缓存之前，必须通过下面的命令创建缓存表：</p>
<figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">python manage<span class="selector-class">.py</span> createcachetable</span><br></pre></td></tr></table></figure>
<p>这将在数据库中创建一个表，该表的格式与 Django 数据库缓存系统期望的一致。该表的表名取自 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/settings/#std:setting-CACHES-LOCATION" target="_blank" rel="noopener"><code>LOCATION</code></a> </p>
<p>如果你正在使用多数据库缓存， <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/django-admin/#django-admin-createcachetable" target="_blank" rel="noopener"><code>createcachetable</code></a> 会对每个缓存创建一个表。</p>
<p>如果你正在使用多数据库， <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/django-admin/#django-admin-createcachetable" target="_blank" rel="noopener"><code>createcachetable</code></a> 将遵循数据库路由的 <code>allow_migrate()</code> 方法。</p>
<p>像 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/django-admin/#django-admin-migrate" target="_blank" rel="noopener"><code>migrate</code></a> 一样， <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/django-admin/#django-admin-createcachetable" target="_blank" rel="noopener"><code>createcachetable</code></a> 不会影响已经存在的表，它只创建缺失的表。</p>
<p>要打印即将运行的 SQL，而不是运行它，请使用 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/django-admin/#cmdoption-createcachetable-dry-run" target="_blank" rel="noopener"><code>createcachetable --dry-run</code></a> 选项。</p>
<ul>
<li>多数据库</li>
</ul>
<p>如果在多数据库中使用缓存，你也需要设置数据库缓存表的路由指令。因为路由的原因，数据库缓存表在 <code>django_cache</code> 应用程序中显示为 <code>CacheEntry</code> 的模型名。这个模型不会出现在模型缓存中，但模型详情可用于路由目的。</p>
<p>比如，下面的路由可以将所有缓存读取操作指向 <code>cache_replica</code> ，并且所有的写操作指向 <code>cache_primary</code>。缓存表将会只同步到 <code>cache_primary</code>。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">CacheRouter</span>:</span></span><br><span class="line">    <span class="string">"""A router to control all database cache operations"""</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">db_for_read</span><span class="params">(self, model, **hints)</span>:</span></span><br><span class="line">        <span class="string">"All cache read operations go to the replica"</span></span><br><span class="line">        <span class="keyword">if</span> model._meta.app_label == <span class="string">'django_cache'</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="string">'cache_replica'</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">None</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">db_for_write</span><span class="params">(self, model, **hints)</span>:</span></span><br><span class="line">        <span class="string">"All cache write operations go to primary"</span></span><br><span class="line">        <span class="keyword">if</span> model._meta.app_label == <span class="string">'django_cache'</span>:</span><br><span class="line">            <span class="keyword">return</span> <span class="string">'cache_primary'</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">None</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">allow_migrate</span><span class="params">(self, db, app_label, model_name=None, **hints)</span>:</span></span><br><span class="line">        <span class="string">"Only install the cache model on primary"</span></span><br><span class="line">        <span class="keyword">if</span> app_label == <span class="string">'django_cache'</span>:</span><br><span class="line">            <span class="keyword">return</span> db == <span class="string">'cache_primary'</span></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">None</span></span><br></pre></td></tr></table></figure>
<p>如果你没有指定路由指向数据库缓存模型，缓存后端将使用 <code>默认</code> 的数据库。当然，如果没使用数据库缓存后端，则无需担心为数据库缓存模型提供路由指令。</p>
<p>19.3 文件系统缓存</p>
<p>基于文件的后端序列化并保存每个缓存值作为单独的文件。要使用此后端，可将 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/settings/#std:setting-CACHES-BACKEND" target="_blank" rel="noopener"><code>BACKEND</code></a> 设置为 <code>&quot;django.core.cache.backends.filebased.FileBasedCache&quot;</code> 并将 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/settings/#std:setting-CACHES-LOCATION" target="_blank" rel="noopener"><code>LOCATION</code></a> 设置为一个合适的路径。比如，在 <code>/var/tmp/django_cache</code> 存储缓存数据，使用以下配置：</p>
<figure class="highlight xquery"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">CACHES = &#123;</span><br><span class="line">    <span class="string">'default'</span>: &#123;</span><br><span class="line">        <span class="string">'BACKEND'</span>: <span class="string">'django.core.cache.backends.filebased.FileBasedCache'</span>,</span><br><span class="line">        <span class="string">'LOCATION'</span>: <span class="string">'/var/tmp/django_cache'</span>,</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>如果使用 Windows 系统，将驱动器号放在路径开头，如下：</p>
<figure class="highlight xquery"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">CACHES = &#123;</span><br><span class="line">    <span class="string">'default'</span>: &#123;</span><br><span class="line">        <span class="string">'BACKEND'</span>: <span class="string">'django.core.cache.backends.filebased.FileBasedCache'</span>,</span><br><span class="line">        <span class="string">'LOCATION'</span>: <span class="string">'c:/foo/bar'</span>,</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>目录路径应该是绝对路径——因此，它应该以文件系统根目录开始。无需担心是否需要以斜杠结尾。</p>
<p>确保这个配置指向的目录存在，并且可由运行 Web 服务器的系统用户读写。继续上面的例子，如果服务器被用户 <code>apache</code> 运行，确保目录 <code>/var/tmp/django_cache</code> 存在并且可被用户 <code>apache</code> 读写。</p>
<p>19.4 本地内存缓存</p>
<p>如果在配置文件中没有指定缓存，那么将默认使用本地内存缓存。如果你想要内存缓存的速度优势，但又没有条件使用 Memcached，那么可以考虑本地内存缓存后端。</p>
<figure class="highlight xquery"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">CACHES = &#123;</span><br><span class="line">    <span class="string">'default'</span>: &#123;</span><br><span class="line">        <span class="string">'BACKEND'</span>: <span class="string">'django.core.cache.backends.locmem.LocMemCache'</span>,</span><br><span class="line">        <span class="string">'LOCATION'</span>: <span class="string">'unique-snowflake'</span>,</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p><a href="https://docs.djangoproject.com/zh-hans/2.2/ref/settings/#std:setting-CACHES-LOCATION" target="_blank" rel="noopener"><code>LOCATION</code></a> 被用于标识各个内存存储。如果只有一个 <code>locmem</code> 缓存，你可以忽略 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/settings/#std:setting-CACHES-LOCATION" target="_blank" rel="noopener"><code>LOCATION</code></a> 。但是如果你有多个本地内存缓存，那么你至少要为其中一个起个名字，以便将它们区分开。这种缓存使用最近最少使用（LRU）的淘汰策略。</p>
<p>注意，每个进程将有它们自己的私有缓存实例，这意味着不存在跨进程的缓存。这也同样意味着本地内存缓存不是特别节省内存，因此它或许不是生成环境的好选择，不过它在开发环境中表现很好。</p>
<p>19.5 虚拟缓存（用于开发模式）</p>
<p>Django 带有一个实际上不是缓存的 “虚拟” 缓存，它只是实现缓存接口，并不做其他操作。如果你有一个正式网站在不同地方使用了重型缓存，但你不想在开发环境使用缓存，而且不想为这个特殊场景而修改代码的时候，这将非常有用。要激活虚拟缓存，像这样设置 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/settings/#std:setting-CACHES-BACKEND" target="_blank" rel="noopener"><code>BACKEND</code></a> 。</p>
<figure class="highlight roboconf"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">CACHES = &#123;</span><br><span class="line">    '<span class="attribute">default'</span>: &#123;</span><br><span class="line">        'BACKEND': 'django<span class="variable">.core</span><span class="variable">.cache</span><span class="variable">.backends</span><span class="variable">.dummy</span><span class="variable">.DummyCache</span>',</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>19.6 使用自定义的缓存后台</p>
<p>虽然 Django 自带一些缓存后端，但有时你也想使用自定义的缓存后端。当使用第三方缓存后端时，使用 Python 导入路径作为 Cache 设置的后端，像这样：</p>
<figure class="highlight xquery"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">CACHES = &#123;</span><br><span class="line">    <span class="string">'default'</span>: &#123;</span><br><span class="line">        <span class="string">'BACKEND'</span>: <span class="string">'path.to.backend'</span>,</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>如果你正在创建自己的后端，你可以使用标准缓存作为参考实现。你在 Django 源代码的 <code>django/core/cache/backends/</code> 目录找到代码。</p>
<p>注意：除非是令人信服的理由，诸如服务器不支持缓存，否则你应该使用 Django 附带的缓存后端。他们经过了良好的测试并易于使用。</p>
<p>19.7  缓存参数</p>
<p>每个缓存后端可以通过额外的参数来控制缓存行为。这些参数在 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/settings/#std:setting-CACHES" target="_blank" rel="noopener"><code>CACHES</code></a> 设置中作为附加键提供。有效参数如下：</p>
<ul>
<li><p>缓存:setting:TIMEOUT <caches-timeout> ：用于缓存的默认超时时间（以秒为单位）。这个参数默认为 <code>300</code> 秒（5分钟）。你可以设置 <code>TIMEOUT</code> 为 <code>None</code>，因此，默认情况下缓存键永不过时。值为 <code>0</code> 会导致键立刻过期（实际上就是不缓存）。</caches-timeout></p>
</li>
<li><p><a href="https://docs.djangoproject.com/zh-hans/2.2/ref/settings/#std:setting-CACHES-OPTIONS" target="_blank" rel="noopener"><code>OPTIONS</code></a> ：任何选项应该传递到缓存后端。有效选项列表将随着每个后端变化，并且由第三方库缓存支持的后端直接传递它们的选项到底层缓存库。</p>
<p>实现自有的淘汰策略的缓存后端（比如 <code>locmem</code>, <code>filesystem</code> 和 <code>database</code> 后端）将遵循以下选项：</p>
<ul>
<li><p><code>MAX_ENTRIES</code> ：删除旧值之前允许缓存的最大条目。默认是 <code>300</code> 。</p>
</li>
<li><p><code>CULL_FREQUENCY</code> ：当达到 <code>MAX_ENTRIES</code> 时被淘汰的部分条目。实际比率为 1 / CULL_FREQUENCY ，当达到 <code>MAX_ENTRIES</code> 时，设置为2就会淘汰一半的条目。这个参数应该是一个整数，默认为3。</p>
<p><code>CULL_FREQUENCY</code> 的值为 <code>0</code> 意味着当达到 <code>MAX_ENTRIES</code> 缓存时，整个缓存都会被清空。在一些后端（尤其是 <code>database</code> ），这会使以更多的缓存未命中为代价来更快的进行淘汰。</p>
</li>
</ul>
<p>Memcached 后端传递 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/settings/#std:setting-CACHES-OPTIONS" target="_blank" rel="noopener"><code>OPTIONS</code></a> 的内容作为键参数到客户端构造函数，从而允许对客户端行为进行更高级的控制。参见下文：</p>
</li>
<li><p><a href="https://docs.djangoproject.com/zh-hans/2.2/ref/settings/#std:setting-CACHES-KEY_PREFIX" target="_blank" rel="noopener"><code>KEY_PREFIX</code></a> ：将自动包含（默认预先添加）到Django 服务器使用的所有缓存键的字符串。</p>
<p>查看 <a href="https://docs.djangoproject.com/zh-hans/2.2/topics/cache/#cache-key-prefixing" target="_blank" rel="noopener">cache documentation</a> 获取更多信息。</p>
</li>
<li><p><a href="https://docs.djangoproject.com/zh-hans/2.2/ref/settings/#std:setting-CACHES-VERSION" target="_blank" rel="noopener"><code>VERSION</code></a> ：通过 Django 服务器生成的缓存键的默认版本号。</p>
<p>查看 <a href="https://docs.djangoproject.com/zh-hans/2.2/topics/cache/#cache-versioning" target="_blank" rel="noopener">cache documentation</a> 获取更多信息。</p>
</li>
<li><p><a href="https://docs.djangoproject.com/zh-hans/2.2/ref/settings/#std:setting-CACHES-KEY_FUNCTION" target="_blank" rel="noopener"><code>KEY_FUNCTION</code></a> ：一个包含指向函数的路径的字符串，该函数定义将如何前缀、版本和键组成最终的缓存键。</p>
<p>查看 <a href="https://docs.djangoproject.com/zh-hans/2.2/topics/cache/#cache-key-transformation" target="_blank" rel="noopener">cache documentation</a> 获取更多信息。</p>
</li>
</ul>
<p>在这个例子中，文件系统后端正被设置成60秒超时时间，并且最大容量是1000条。</p>
<figure class="highlight xquery"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">CACHES = &#123;</span><br><span class="line">    <span class="string">'default'</span>: &#123;</span><br><span class="line">        <span class="string">'BACKEND'</span>: <span class="string">'django.core.cache.backends.filebased.FileBasedCache'</span>,</span><br><span class="line">        <span class="string">'LOCATION'</span>: <span class="string">'/var/tmp/django_cache'</span>,</span><br><span class="line">        <span class="string">'TIMEOUT'</span>: <span class="number">60</span>,</span><br><span class="line">        <span class="string">'OPTIONS'</span>: &#123;</span><br><span class="line">            <span class="string">'MAX_ENTRIES'</span>: <span class="number">1000</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这个的例子是基于 <code>python-memcached</code> 后端的设置，对象大小限制在 2MB ：</p>
<figure class="highlight xquery"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">CACHES = &#123;</span><br><span class="line">    <span class="string">'default'</span>: &#123;</span><br><span class="line">        <span class="string">'BACKEND'</span>: <span class="string">'django.core.cache.backends.memcached.MemcachedCache'</span>,</span><br><span class="line">        <span class="string">'LOCATION'</span>: <span class="string">'127.0.0.1:11211'</span>,</span><br><span class="line">        <span class="string">'OPTIONS'</span>: &#123;</span><br><span class="line">            <span class="string">'server_max_value_length'</span>: <span class="number">1024</span> * <span class="number">1024</span> * <span class="number">2</span>,</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>这个例子是基于 <code>pylibmc</code> 后端的设置，改设置支持二进制协议、SASL 验证和 <code>ketama</code>行为模式：</p>
<figure class="highlight xquery"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">CACHES = &#123;</span><br><span class="line">    <span class="string">'default'</span>: &#123;</span><br><span class="line">        <span class="string">'BACKEND'</span>: <span class="string">'django.core.cache.backends.memcached.PyLibMCCache'</span>,</span><br><span class="line">        <span class="string">'LOCATION'</span>: <span class="string">'127.0.0.1:11211'</span>,</span><br><span class="line">        <span class="string">'OPTIONS'</span>: &#123;</span><br><span class="line">            <span class="string">'binary'</span>: True,</span><br><span class="line">            <span class="string">'username'</span>: <span class="string">'user'</span>,</span><br><span class="line">            <span class="string">'password'</span>: <span class="string">'pass'</span>,</span><br><span class="line">            <span class="string">'behaviors'</span>: &#123;</span><br><span class="line">                <span class="string">'ketama'</span>: True,</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>更多缓存知识访问&gt;&gt; <a href="https://docs.djangoproject.com/zh-hans/2.2/topics/cache/" target="_blank" rel="noopener">https://docs.djangoproject.com/zh-hans/2.2/topics/cache/</a></p>
<p>（20）发送邮件</p>
<p>20.1 快速上手,仅需两行代码:</p>
<figure class="highlight clean"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.core.mail <span class="keyword">import</span> send_mail</span><br><span class="line"></span><br><span class="line">send_mail(</span><br><span class="line">    <span class="string">'Subject here'</span>,</span><br><span class="line">    <span class="string">'Here is the message.'</span>,</span><br><span class="line">    <span class="string">'from@example.com'</span>,</span><br><span class="line">    [<span class="string">'to@example.com'</span>],</span><br><span class="line">    fail_silently=<span class="literal">False</span>,</span><br><span class="line">)</span><br></pre></td></tr></table></figure>
<p>邮件是通过 SMTP 主机和端口发送的，由配置项 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/settings/#std:setting-EMAIL_HOST" target="_blank" rel="noopener"><code>EMAIL_HOST</code></a> 和 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/settings/#std:setting-EMAIL_PORT" target="_blank" rel="noopener"><code>EMAIL_PORT</code></a> 指定。如果配置了 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/settings/#std:setting-EMAIL_HOST_USER" target="_blank" rel="noopener"><code>EMAIL_HOST_USER</code></a> 和 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/settings/#std:setting-EMAIL_HOST_PASSWORD" target="_blank" rel="noopener"><code>EMAIL_HOST_PASSWORD</code></a> ，那么它们将被用来验证 SMTP 服务器。配置项 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/settings/#std:setting-EMAIL_USE_TLS" target="_blank" rel="noopener"><code>EMAIL_USE_TLS</code></a> 和 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/settings/#std:setting-EMAIL_USE_SSL" target="_blank" rel="noopener"><code>EMAIL_USE_SSL</code></a> 控制是否使用安全连接。发送邮件最简单的方式就是使用 <code>django.core.mail.send_mail()</code>。</p>
<p>send_mail():<code>send_mail</code>(<em>subject</em>, <em>message</em>, <em>from_email</em>, <em>recipient_list</em>, <em>fail_silently=False</em>, <em>auth_user=None</em>, <em>auth_password=None</em>, <em>connection=None</em>, <em>html_message=None</em>)</p>
<ul>
<li>参数 <code>subject</code>, <code>message</code>, <code>from_email</code> 和 <code>recipient_list</code> 是必须的。</li>
<li><code>recipient_list</code>: 一个字符串列表，每项都是一个邮箱地址。<code>recipient_list</code>中的每个成员都可以在邮件的 “收件人:” 中看到其他的收件人。</li>
<li><code>fail_silently</code>: 为 <code>False</code>， <code>send_mail()</code> 发生错误时抛出 <a href="https://docs.python.org/3/library/smtplib.html#smtplib.SMTPException" target="_blank" rel="noopener"><code>smtplib.SMTPException</code></a> 。可在 <a href="https://docs.python.org/3/library/smtplib.html#module-smtplib" target="_blank" rel="noopener"><code>smtplib</code></a> 文档找到一系列可能的异常，它们都是 <a href="https://docs.python.org/3/library/smtplib.html#smtplib.SMTPException" target="_blank" rel="noopener"><code>SMTPException</code></a> 的子类。</li>
<li><code>auth_user</code>: 可选的用户名，用于验证登陆 SMTP 服务器。 若未提供，Django 会使用 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/settings/#std:setting-EMAIL_HOST_USER" target="_blank" rel="noopener"><code>EMAIL_HOST_USER</code></a> 指定的值。</li>
<li><code>auth_password</code>: 可选的密码，用于验证登陆 SMTP 服务器。若未提供， Django 会使用 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/settings/#std:setting-EMAIL_HOST_PASSWORD" target="_blank" rel="noopener"><code>EMAIL_HOST_PASSWORD</code></a> 指定的值。</li>
<li><code>connection</code>: 可选参数，发送邮件使用的后端。若未指定，则使用默认的后端。查询 <a href="https://docs.djangoproject.com/zh-hans/2.2/topics/email/#topic-email-backends" target="_blank" rel="noopener">邮件后端</a> 文档获取更多细节。</li>
<li><code>html_message</code>: 若提供了 <code>html_message</code>，会使邮件成为 <em>multipart/alternative</em> 的实例， <code>message</code> 的内容类型则是 <em>text/plain</em> ，并且 <code>html_message</code> 的内容类型是 <em>text/html</em> 。</li>
</ul>
<p>返回值会是成功发送的信息的数量（只能是 <code>0</code> 或 <code>1</code> ，因为同时只能发送一条消息）。</p>
<p>20.2 批量发送邮件</p>
<p><code>django.core.mail.send_mass_mail()</code> 用于批量发送邮件。</p>
<p>send_mass_mail():(<em>datatuple</em>, <em>fail_silently=False</em>, <em>auth_user=None</em>, <em>auth_password=None</em>, <em>connection=None</em>)</p>
<p><code>datatuple</code> 是一个元组，形式如下:</p>
<figure class="highlight clojure"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(<span class="name">subject</span>, message, from_email, recipient_list)</span><br></pre></td></tr></table></figure>
<p><code>datatuple</code> 参数的每个元素会生成一份独立的邮件内容。就像 <a href="https://docs.djangoproject.com/zh-hans/2.2/topics/email/#django.core.mail.send_mail" target="_blank" rel="noopener"><code>send_mail()</code></a> 中的一样， <code>recipient_list</code> 中的每个收件人会在邮件的 “收件人:” 中看到其他收件人的地址一样.举个例子，以下代码会向两个不同的收件人列表发送两封不同的邮件，却复用了同一条连接:返回值是成功发送的消息的数量。</p>
<figure class="highlight sml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">message1 = (<span class="symbol">'Subject</span> here', <span class="symbol">'Here</span> is the message', <span class="symbol">'from</span>@example.com', [<span class="symbol">'first</span>@example.com', <span class="symbol">'other</span>@example.com'])</span><br><span class="line">message2 = (<span class="symbol">'Another</span> <span class="type">Subject'</span>, <span class="symbol">'Here</span> is another message', <span class="symbol">'from</span>@example.com', [<span class="symbol">'second</span>@test.com'])</span><br><span class="line">send_mass_mail((message1, message2), fail_silently=<span class="type">False</span>)</span><br></pre></td></tr></table></figure>
<p>20.3 显示全部收件人与独立收件人</p>
<p>以下发送了一封邮件给 <a href="mailto:john%40example.com" target="_blank" rel="noopener">john@example.com</a> 和 <a href="mailto:jane%40example.com" target="_blank" rel="noopener">jane@example.com</a>，他们都出现在 “收件人:”:</p>
<figure class="highlight scala"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">send_mail(</span><br><span class="line">    <span class="symbol">'Subjec</span>t',</span><br><span class="line">    <span class="symbol">'Message</span>.',</span><br><span class="line">    <span class="symbol">'from</span><span class="meta">@example</span>.com',</span><br><span class="line">    [<span class="symbol">'john</span><span class="meta">@example</span>.com', <span class="symbol">'jane</span><span class="meta">@example</span>.com'],</span><br><span class="line">)</span><br></pre></td></tr></table></figure>
<p>以下分别发送了一封邮件给 <a href="mailto:john%40example.com" target="_blank" rel="noopener">john@example.com</a> 和 <a href="mailto:jane%40example.com" target="_blank" rel="noopener">jane@example.com</a>，他们收到了独立的邮件:</p>
<figure class="highlight lisp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">datatuple = (</span><br><span class="line">    ('Subject', 'Message.', 'from@example.com', ['john@example.com']),</span><br><span class="line">    ('Subject', 'Message.', 'from@example.com', ['jane@example.com']),</span><br><span class="line">)</span><br><span class="line">send_mass_mail(<span class="name">datatuple</span>)</span><br></pre></td></tr></table></figure>
<p>20.4  防止头注入</p>
<p><a href="http://www.nyphp.org/phundamentals/8_Preventing-Email-Header-Injection" target="_blank" rel="noopener">Header injection</a> 是一个开发漏洞，攻击者可以利用它在邮件头插入额外信息，以控制脚本生成的邮件中的 “收件人:” 和 “发件人:” 内容。Django 的邮件函数包含了以上所有的反头注入功能，通过在头中禁止新的行。如果 <code>subject</code>， <code>from_email</code> 或 <code>recipient_list</code> 包含了新行（不管是 Unix，Windows 或 Mac 格式中的哪一种），邮件函数（比如 <a href="https://docs.djangoproject.com/zh-hans/2.2/topics/email/#django.core.mail.send_mail" target="_blank" rel="noopener"><code>send_mail()</code></a> ）都会抛出一个 <code>django.core.mail.BadHeaderError</code> （ <code>ValueError</code> 的子类），这会中断邮件发送。你需要在将参数传给邮件函数前确保数据的有效性和合法性。</p>
<p>如果邮件的 <code>内容</code> 的开始部分包含了邮件头信息，这些头信息只会作为邮件内容原样打印。以下是一个实例视图，从请求的 POST 数据中获取 <code>subject</code>， <code>message</code> 和 <code>from_email</code>，并将其发送至 <a href="mailto:admin%40example.com" target="_blank" rel="noopener">admin@example.com</a> ，成功后再重定向至 “/contact/thanks/“</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.core.mail <span class="keyword">import</span> BadHeaderError, send_mail</span><br><span class="line"><span class="keyword">from</span> django.http <span class="keyword">import</span> HttpResponse, HttpResponseRedirect</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">send_email</span><span class="params">(request)</span>:</span></span><br><span class="line">    subject = request.POST.get(<span class="string">'subject'</span>, <span class="string">''</span>)</span><br><span class="line">    message = request.POST.get(<span class="string">'message'</span>, <span class="string">''</span>)</span><br><span class="line">    from_email = request.POST.get(<span class="string">'from_email'</span>, <span class="string">''</span>)</span><br><span class="line">    <span class="keyword">if</span> subject <span class="keyword">and</span> message <span class="keyword">and</span> from_email:</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            send_mail(subject, message, from_email, [<span class="string">'admin@example.com'</span>])</span><br><span class="line">        <span class="keyword">except</span> BadHeaderError:</span><br><span class="line">            <span class="keyword">return</span> HttpResponse(<span class="string">'Invalid header found.'</span>)</span><br><span class="line">        <span class="keyword">return</span> HttpResponseRedirect(<span class="string">'/contact/thanks/'</span>)</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="comment"># In reality we'd use a form class</span></span><br><span class="line">        <span class="comment"># to get proper validation errors.</span></span><br><span class="line">        <span class="keyword">return</span> HttpResponse(<span class="string">'Make sure all fields are entered and valid.'</span>)</span><br></pre></td></tr></table></figure>
<p>关于发送邮件的单元测试资料，参见测试文档中 <a href="https://docs.djangoproject.com/zh-hans/2.2/topics/testing/tools/#topics-testing-email" target="_blank" rel="noopener">Email services</a> 章节。</p>
<p>（21）分页</p>
<p>21.1 分页基本操作</p>
<figure class="highlight ruby"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;&gt;</span>&gt; from django.core.paginator import Paginator</span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; objects = [<span class="string">'john'</span>, <span class="string">'paul'</span>, <span class="string">'george'</span>, <span class="string">'ringo'</span>]</span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; p = Paginator(objects, <span class="number">2</span>)</span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; p.count</span><br><span class="line"><span class="number">4</span></span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; p.num_pages</span><br><span class="line"><span class="number">2</span></span><br><span class="line"></span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; page2 = p.page(<span class="number">2</span>)</span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; page2.object_list</span><br><span class="line">[<span class="string">'george'</span>, <span class="string">'ringo'</span>]</span><br><span class="line"></span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; page2.has_next()</span><br><span class="line">False</span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; page2.has_previous()</span><br><span class="line">True</span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; page2.has_other_pages()</span><br><span class="line">True</span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; page2.previous_page_number()</span><br><span class="line"><span class="number">1</span></span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; page2.start_index() <span class="comment"># The 1-based index of the first item on this page</span></span><br><span class="line"><span class="number">3</span></span><br><span class="line"><span class="meta">&gt;&gt;</span>&gt; page2.end_index() <span class="comment"># The 1-based index of the last item on this page</span></span><br><span class="line"><span class="number">4</span></span><br></pre></td></tr></table></figure>
<p>21.2 在视图中使用 <code>Paginator</code></p>
<p>The view function looks like this:</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> django.core.paginator <span class="keyword">import</span> Paginator</span><br><span class="line"><span class="keyword">from</span> django.shortcuts <span class="keyword">import</span> render</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">listing</span><span class="params">(request)</span>:</span></span><br><span class="line">    contact_list = Contacts.objects.all()</span><br><span class="line">    paginator = Paginator(contact_list, <span class="number">25</span>) <span class="comment"># Show 25 contacts per page</span></span><br><span class="line"></span><br><span class="line">    page = request.GET.get(<span class="string">'page'</span>)</span><br><span class="line">    contacts = paginator.get_page(page)</span><br><span class="line">    <span class="keyword">return</span> render(request, <span class="string">'list.html'</span>, &#123;<span class="string">'contacts'</span>: contacts&#125;)</span><br></pre></td></tr></table></figure>
<p>In the template <code>list.html</code>, you’ll want to include navigation between pages along with any interesting information from the objects themselves:</p>
<figure class="highlight django"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="xml"></span><span class="template-tag">&#123;% <span class="name"><span class="name">for</span></span> contact <span class="keyword">in</span> contacts %&#125;</span><span class="xml"></span></span><br><span class="line"><span class="xml">    </span><span class="comment">&#123;# Each "contact" is a Contact model object. #&#125;</span><span class="xml"></span></span><br><span class="line"><span class="xml">    </span><span class="template-variable">&#123;&#123; contact.full_name|<span class="name">upper</span> &#125;&#125;</span><span class="xml"><span class="tag">&lt;<span class="name">br</span>&gt;</span></span></span><br><span class="line"><span class="xml">    ...</span></span><br><span class="line"><span class="xml"></span><span class="template-tag">&#123;% <span class="name"><span class="name">endfor</span></span> %&#125;</span><span class="xml"></span></span><br><span class="line"><span class="xml"></span></span><br><span class="line"><span class="xml"><span class="tag">&lt;<span class="name">div</span> <span class="attr">class</span>=<span class="string">"pagination"</span>&gt;</span></span></span><br><span class="line"><span class="xml">    <span class="tag">&lt;<span class="name">span</span> <span class="attr">class</span>=<span class="string">"step-links"</span>&gt;</span></span></span><br><span class="line"><span class="xml">        </span><span class="template-tag">&#123;% <span class="name"><span class="name">if</span></span> contacts.has_previous %&#125;</span><span class="xml"></span></span><br><span class="line"><span class="xml">            <span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">"?page=1"</span>&gt;</span>&amp;laquo; first<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span></span><br><span class="line"><span class="xml">            <span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">"?page=</span></span></span><span class="template-variable">&#123;&#123; contacts.previous_page_number &#125;&#125;</span><span class="xml"><span class="tag"><span class="string">"</span>&gt;</span>previous<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span></span><br><span class="line"><span class="xml">        </span><span class="template-tag">&#123;% <span class="name"><span class="name">endif</span></span> %&#125;</span><span class="xml"></span></span><br><span class="line"><span class="xml"></span></span><br><span class="line"><span class="xml">        <span class="tag">&lt;<span class="name">span</span> <span class="attr">class</span>=<span class="string">"current"</span>&gt;</span></span></span><br><span class="line"><span class="xml">            Page </span><span class="template-variable">&#123;&#123; contacts.number &#125;&#125;</span><span class="xml"> of </span><span class="template-variable">&#123;&#123; contacts.paginator.num_pages &#125;&#125;</span><span class="xml">.</span></span><br><span class="line"><span class="xml">        <span class="tag">&lt;/<span class="name">span</span>&gt;</span></span></span><br><span class="line"><span class="xml"></span></span><br><span class="line"><span class="xml">        </span><span class="template-tag">&#123;% <span class="name"><span class="name">if</span></span> contacts.has_next %&#125;</span><span class="xml"></span></span><br><span class="line"><span class="xml">            <span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">"?page=</span></span></span><span class="template-variable">&#123;&#123; contacts.next_page_number &#125;&#125;</span><span class="xml"><span class="tag"><span class="string">"</span>&gt;</span>next<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span></span><br><span class="line"><span class="xml">            <span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">"?page=</span></span></span><span class="template-variable">&#123;&#123; contacts.paginator.num_pages &#125;&#125;</span><span class="xml"><span class="tag"><span class="string">"</span>&gt;</span>last &amp;raquo;<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span></span><br><span class="line"><span class="xml">        </span><span class="template-tag">&#123;% <span class="name"><span class="name">endif</span></span> %&#125;</span><span class="xml"></span></span><br><span class="line"><span class="xml">    <span class="tag">&lt;/<span class="name">span</span>&gt;</span></span></span><br><span class="line"><span class="xml"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span><br></pre></td></tr></table></figure>
<p>21.3 相关方法与属性</p>
<ul>
<li>Page.has_next()</li>
<li><code>Page.has_previous</code>()</li>
<li><code>Page.has_other_pages</code>()</li>
<li><code>Page.next_page_number</code>()</li>
<li><code>Page.previous_page_number</code>()</li>
<li><code>Page.start_index</code>()</li>
<li><code>Page.end_index</code>()</li>
<li><code>Page.object_list</code>：此页上的对象列表。</li>
<li>Page.number：此页的基于 1 的页码。</li>
<li>Page.paginator：关联的 Paginator 对象。</li>
</ul>
<p>（22）性能与优化</p>
<p>22.1 性能优化介绍</p>
<p>清楚地理解你所说的“绩效”是什么很重要，因为它不仅仅是一个指标。提高速度可能是程序最明显的目标，但有时可能会寻求其他性能改进，<font style="color:red">例如降低内存消耗或减少对数据库或网络的要求。一个领域的改进通常会提高另一个领域的性能，但并不总是如此；有时甚至会牺牲另一个领域的性能。</font>例如，一个程序速度的提高可能会导致它使用更多的内存。更糟糕的是，如果速度提高太过内存不足，以致于系统开始耗尽内存，那么你所做的弊大于利。还有其他的权衡。你自己的时间是一个宝贵的资源，比CPU时间更宝贵。一些改进可能太难实现，或者可能影响代码的可移植性或可维护性。并非所有的性能改进都值得付出努力。所以，你需要知道你的目标是什么样的性能改进，你也需要知道你有一个很好的理由去瞄准那个方向——而且你需要：</p>
<p>django-debug-toolbar  <a href="https://github.com/jazzband/django-debug-toolbar/" target="_blank" rel="noopener">https://github.com/jazzband/django-debug-toolbar/</a>  是一个非常方便的工具，它可以深入了解您的代码正在做什么以及花费了多少时间。特别是它可以显示您的页面生成的所有SQL查询，以及每个查询所用的时间。第三方面板也可用于工具栏，可以（例如）报告缓存性能和模板呈现时间。</p>
<p>22.2 性能优化的几个方面</p>
<ul>
<li>内存与索引</li>
<li>数据库查询优化</li>
<li>中间件优化</li>
<li>访问时间</li>
<li>缓存</li>
<li>禁用 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/settings/#std:setting-DEBUG" target="_blank" rel="noopener"><code>DEBUG = False</code></a> </li>
</ul>
<p>（23）WSGI部署Django</p>
<p>23.1 使用 WSGI 进行部署</p>
<p>WSGI，PythonWeb服务器<a href="https://baike.baidu.com/item/%E7%BD%91%E5%85%B3" target="_blank" rel="noopener">网关</a>接口（Python Web Server Gateway Interface，缩写为WSGI)是Python应用程序或框架和Web服务器之间的一种接口，已经被广泛接受, 它已基本达成它的可移植性方面的目标。WSGI是作为Web服务器与Web应用程序或应用框架之间的一种低级别的接口，以提升可移植Web应用开发的共同点。WSGI是基于现存的[[CGI]]标准而设计的。</p>
<ul>
<li>application对象</li>
</ul>
<p>用 WSGI 部署的关键是 <code>application</code> callable，应用服务器用它与你的代码交互。 <code>application</code> callable 一般以一个位于 Python 模块中，名为 <code>application</code> 的对象的形式提供，且对服务器可见。<a href="https://docs.djangoproject.com/zh-hans/2.2/ref/django-admin/#django-admin-startproject" target="_blank" rel="noopener"><code>startproject</code></a> 命令创建了文件 <code>&lt;project_name&gt;/wsgi.py</code>，其中包含了 <code>application</code> callable。Django 开发服务器和生产环境的 WSGI 部署都用到了它。</p>
<p>WSGI 服务器从其配置中获取 <code>application</code> callable 的路径。Django 的默认服务器（ <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/django-admin/#django-admin-runserver" target="_blank" rel="noopener"><code>runserver</code></a> 命令），从配置项 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/settings/#std:setting-WSGI_APPLICATION" target="_blank" rel="noopener"><code>WSGI_APPLICATION</code></a> 中获取。默认值是 <code>&lt;project_name&gt;.wsgi.application</code>，指向 <code>&lt;project_name&gt;/wsgi.py</code> 中的 <code>application</code> callable。</p>
<ul>
<li>配置setting模块</li>
</ul>
<p><code>wsgi.py</code> 默认将其设置为 <code>mysite.settings</code>， <code>mysite</code> 即工程名字。这就是 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/django-admin/#django-admin-runserver" target="_blank" rel="noopener"><code>runserver</code></a> 默认的发现默认配置行为。</p>
<p>注解：由于环境变量是进程级的，所以如果在同一进程运行多个 Django 站点将出错。这在使用 mod_wsgi 时会出现。要避免此问题，为每个站点在后台进程使用 mod_wsgi 的后台模式，或者在 <code>wsgi.py</code> 中通过 <code>os.environ[&quot;DJANGO_SETTINGS_MODULE&quot;]= &quot;mysite.settings&quot;</code> 重写来自环境变量的值。</p>
<ul>
<li>应用WSGI中间件</li>
</ul>
<p>要应用 <a href="https://www.python.org/dev/peps/pep-3333/#middleware-components-that-play-both-sides" target="_blank" rel="noopener">WSGI 中间层</a>，你只需简单包裹应用对象。举个例子，你可以在 <code>wsgi.py</code> 末尾添加以下代码:</p>
<figure class="highlight applescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> helloworld.wsgi import HelloWorldApplication</span><br><span class="line"><span class="built_in">application</span> = HelloWorldApplication(<span class="built_in">application</span>)</span><br></pre></td></tr></table></figure>
<p>如果你想将 Django 应用于一个 WSGI 应用或其它框架联合起来，可以用自定义 WSGI 应用替换 Django 的 WSGI 应用，前者会在稍晚时候将任务委托给 WSGI 应用。</p>
<p>23.2 使用 Apache 和 <code>mod_wsgi</code> 托管 Django</p>
<p>mod_wsgi 是一个 Apache 模块，它可以管理任何 Python <a href="https://wsgi.readthedocs.io/en/latest/" target="_blank" rel="noopener">WSGI</a> 应用，包括 Django。Django 支持所有支持 mod_wsgi 的 Apache 版本。<a href="https://modwsgi.readthedocs.io/en/develop/installation.html" target="_blank" rel="noopener">官方 mod_wsgi 文档</a> 介绍了如何使用 mod_wsgi 的全部细节。你可能更喜欢从 <a href="https://modwsgi.readthedocs.io/" target="_blank" rel="noopener">安装和配置文档</a> 开始。</p>
<p>安装并激活 mod_wsgi 后，编辑 Apache 服务器的 <a href="https://wiki.apache.org/httpd/DistrosDefaultLayout" target="_blank" rel="noopener">httpd.conf</a> 文件，并添加以下内容。若你正在使用的 Apache 版本号早于 2.4，用 <code>Allow from all</code> 替换 <code>Require allgranted</code>，并在其上添加一行 <code>Order deny,allow</code>。</p>
<figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">WSGIScriptAlias / /path/<span class="keyword">to</span>/mysite.<span class="keyword">com</span>/mysite/wsgi.<span class="keyword">py</span></span><br><span class="line">WSGIPythonHome /path/<span class="keyword">to</span>/venv</span><br><span class="line">WSGIPythonPath /path/<span class="keyword">to</span>/mysite.<span class="keyword">com</span></span><br><span class="line"></span><br><span class="line">&lt;Directory /path/<span class="keyword">to</span>/mysite.<span class="keyword">com</span>/mysite&gt;</span><br><span class="line">&lt;Files wsgi.<span class="keyword">py</span>&gt;</span><br><span class="line">Require <span class="keyword">all</span> granted</span><br><span class="line">&lt;/Files&gt;</span><br><span class="line">&lt;/Directory&gt;</span><br></pre></td></tr></table></figure>
<ul>
<li><code>WSGIScriptAlias</code> 行的第一项是你所期望的应用所在的基础 URL 路径（ <code>/</code> 根 url），第二项是 “WSGI 文件” 的位置——一般位于项目包之内（本例中是 <code>mysite</code>）。这告诉 Apache 用该文件中定义的 WSGI 应用响应指定 URL 下的请求。</li>
<li>如果你在某个 <a href="https://virtualenv.pypa.io/" target="_blank" rel="noopener">virtualenv</a> 内为应用安装项目的 Python 依赖，将该 virtualenv 的路径添加至 <code>WSGIPythonHome</code> 。参考 <a href="https://modwsgi.readthedocs.io/en/develop/user-guides/virtual-environments.html" target="_blank" rel="noopener">mod_wsgi virtualenv 指引</a> 获取更多细节。</li>
<li><code>WSGIPythonPath</code> 行确保你的项目包能从 Python path 导入；换句话说， <code>importmysite</code> 能正常工作。</li>
<li><code>&lt;Directory&gt;</code> 片段仅确保 Apache 能访问 <code>wsgi.py</code> 文件。</li>
<li>下一步，我们需要确认 <code>wsgi.py</code> 文件包含一个 WSGI 应用对象。从 Django 1.4 起， <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/django-admin/#django-admin-startproject" target="_blank" rel="noopener"><code>startproject</code></a> 会自动创建；换而言之，你无需手动创建。查阅 <a href="https://docs.djangoproject.com/zh-hans/2.2/howto/deployment/wsgi/" target="_blank" rel="noopener">WSGI 概述文档</a> 获取你需要配置的默认内容，以及其它可配置项。</li>
</ul>
<p>注意1：</p>
<blockquote>
<p>如果多个 Django 站点运行在同一 mod_wsgi 进程，它们会共用最先启动的站点配置。能通过以下修改改变行为:</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;</span><span class="bash"> os.environ.setdefault(<span class="string">"DJANGO_SETTINGS_MODULE"</span>, <span class="string">"&#123;&#123; project_name &#125;&#125;.settings"</span>)</span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> </span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash"></span></span><br></pre></td></tr></table></figure>
</blockquote>
<p>&gt;</p>
<blockquote>
<p><code>wsgi.py</code> 中也这么改:</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;</span><span class="bash"> os.environ[<span class="string">"DJANGO_SETTINGS_MODULE"</span>] = <span class="string">"&#123;&#123; project_name &#125;&#125;.settings"</span></span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> </span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash"></span></span><br></pre></td></tr></table></figure>
</blockquote>
<p>&gt;</p>
<blockquote>
<p>或通过 <a href="https://docs.djangoproject.com/zh-hans/2.2/howto/deployment/wsgi/modwsgi/#daemon-mode" target="_blank" rel="noopener">使用 mod_wsgi 的后台模式</a> 确保每个站点都运行于独立的后台进程。</p>
</blockquote>
<p>注意2：</p>
<blockquote>
<p>为文件上传修复 <code>UnicodeEncodeError</code></p>
<p>上传名称包含非 ASCII 字符的文件时，若抛出 <code>UnicodeEncodeError</code>，确认 Apache 是否被正确配置，能接受非 ASCII 文件名:</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&gt;</span><span class="bash"> <span class="built_in">export</span> LANG=<span class="string">'en_US.UTF-8'</span></span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> <span class="built_in">export</span> LC_ALL=<span class="string">'en_US.UTF-8'</span></span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash"> </span></span><br><span class="line"><span class="meta">&gt;</span><span class="bash"></span></span><br></pre></td></tr></table></figure>
</blockquote>
<p>&gt;</p>
<blockquote>
<p>常见的配置文件路径是 <code>/etc/apache2/envvars</code>。</p>
<p>参考 Unicode 参考指引的 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/unicode/#unicode-files" target="_blank" rel="noopener">Files</a> 章节获取细节信息。</p>
</blockquote>
<ul>
<li>使用 <code>mod_wsgi</code> 后台模式</li>
</ul>
<p>为了创建必要的后台进程组并在其中运行 Django 实例，你需要添加合适的 <code>WSGIDaemonProcess</code>和 <code>WSGIProcessGroup</code> 指令。上述配置在你使用后台模式时需要点小修改，即你不能使用 <code>WSGIPythonPath</code>；作为替换，你要在 <code>WSGIDaemonProcess</code> 中添加 <code>python-path</code> 选项，例如：</p>
<figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">WSGIDaemonProcess example.<span class="keyword">com</span> <span class="keyword">python</span>-home=/path/<span class="keyword">to</span>/venv <span class="keyword">python</span>-path=/path/<span class="keyword">to</span>/mysite.<span class="keyword">com</span></span><br><span class="line">WSGIProcessGroup example.<span class="keyword">com</span></span><br></pre></td></tr></table></figure>
<p>如果你想在子目录中开放你的项目（本例中 <code>https://example.com/mysite</code>），你可在上述配置中添加 <code>WSGIScriptAlias</code>：</p>
<figure class="highlight gradle"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">WSGIScriptAlias <span class="regexp">/mysite /</span>path<span class="regexp">/to/my</span>site.com<span class="regexp">/mysite/</span>wsgi.py process-<span class="keyword">group</span>=example.com</span><br></pre></td></tr></table></figure>
<p>参考官方 mod_wsgi 文档获取 <a href="https://modwsgi.readthedocs.io/en/develop/user-guides/quick-configuration-guide.html#delegation-to-daemon-process" target="_blank" rel="noopener">配置后台模式的细节</a>。</p>
<p>23.3 Apache 利用 Django 的用户数据库进行验证</p>
<p>使用 Apache 时，保持多个身份认证数据同步是一个常见的问题，你可以让 Apache 直接使用 Django 的 <a href="https://docs.djangoproject.com/zh-hans/2.2/topics/auth/" target="_blank" rel="noopener">验证系统</a>。这要求 Apache 版本 &gt;= 2.2，且 mod_wsgi &gt;= 2.0。例如这样：</p>
<ul>
<li>仅为已授权的用户直接从 Apache 提供 static/media 文件。</li>
<li>仅为有特定权限的 Django 用户提供 Subversion 仓库访问。</li>
<li>允许某些用户连接到 <a href="https://httpd.apache.org/docs/2.2/mod/mod_dav.html" target="_blank" rel="noopener">mod_dav</a> 创建的 WebDAV 共享。</li>
</ul>
<p>确保你已按照 <a href="https://docs.djangoproject.com/zh-hans/2.2/howto/deployment/wsgi/modwsgi/" target="_blank" rel="noopener">Apache 配合 mod_wsgi</a> 文档正确安装并激活了 mod_wsgi。然后，编辑 Apache 配置，添加只允许授权用户查看的位置：</p>
<figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">WSGIScriptAlias / /path/<span class="keyword">to</span>/mysite.<span class="keyword">com</span>/mysite/wsgi.<span class="keyword">py</span></span><br><span class="line">WSGIPythonPath /path/<span class="keyword">to</span>/mysite.<span class="keyword">com</span></span><br><span class="line"></span><br><span class="line">WSGIProcessGroup %&#123;GLOBAL&#125;</span><br><span class="line">WSGIApplicationGroup %&#123;GLOBAL&#125;</span><br><span class="line"></span><br><span class="line">&lt;Location <span class="string">"/secret"</span>&gt;</span><br><span class="line">    AuthType Basic</span><br><span class="line">    AuthName <span class="string">"Top Secret"</span></span><br><span class="line">    Require valid-user</span><br><span class="line">    AuthBasicProvider wsgi</span><br><span class="line">    WSGIAuthUserScript /path/<span class="keyword">to</span>/mysite.<span class="keyword">com</span>/mysite/wsgi.<span class="keyword">py</span></span><br><span class="line">&lt;/Location&gt;</span><br></pre></td></tr></table></figure>
<p><code>WSGIAuthUserScript</code> 指令告诉 mod_wsgi 在指定 wsgi 脚本中执行 <code>check_password</code> 函数，并传递从提示符获取的用户名和密码。在本例中， <code>WSGIAuthUserScript</code> 与 <code>WSGIScriptAlias</code> 一样，后者 <a href="https://docs.djangoproject.com/zh-hans/2.2/howto/deployment/wsgi/" target="_blank" rel="noopener">由 django-admin startproject 创建</a>，定义了应用。</p>
<p>最后，编辑 WSGI 脚本 <code>mysite.wsgi</code>，通过导入 <code>check_password</code> 函数，将 Apache 的认证授权机制接续在你站点的授权机制之后:</p>
<figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">import os</span><br><span class="line"></span><br><span class="line">os<span class="selector-class">.environ</span>[<span class="string">'DJANGO_SETTINGS_MODULE'</span>] = <span class="string">'mysite.settings'</span></span><br><span class="line"></span><br><span class="line">from django<span class="selector-class">.contrib</span><span class="selector-class">.auth</span><span class="selector-class">.handlers</span><span class="selector-class">.modwsgi</span> import check_password</span><br><span class="line"></span><br><span class="line">from django<span class="selector-class">.core</span><span class="selector-class">.handlers</span><span class="selector-class">.wsgi</span> import WSGIHandler</span><br><span class="line">application = WSGIHandler()</span><br></pre></td></tr></table></figure>
<p>以 <code>/secret/</code> 开头的请求现在会要求用户认证。</p>
<p>mod_wsgi <a href="https://modwsgi.readthedocs.io/en/develop/user-guides/access-control-mechanisms.html" target="_blank" rel="noopener">可达性控制机制文档</a> 提供了其它授权机制和方法的更多细节和信息。</p>
<h3 id="利用-mod-wsgi-和-Django-用户组-groups-进行授权¶"><a href="#利用-mod-wsgi-和-Django-用户组-groups-进行授权¶" class="headerlink" title="利用 mod_wsgi 和 Django 用户组(groups)进行授权¶"></a>利用 <code>mod_wsgi</code> 和 Django 用户组(groups)进行授权<a href="https://docs.djangoproject.com/zh-hans/2.2/howto/deployment/wsgi/apache-auth/#authorization-with-mod-wsgi-and-django-groups" target="_blank" rel="noopener">¶</a></h3><p>mod_wsgi 也提供了将组成员限制至特定路径的功能。</p>
<p>在本例中，Apache 配置应该看起来像这样：</p>
<figure class="highlight vim"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">WSGIScriptAlias / /path/<span class="keyword">to</span>/mysite.<span class="keyword">com</span>/mysite/wsgi.<span class="keyword">py</span></span><br><span class="line"></span><br><span class="line">WSGIProcessGroup %&#123;GLOBAL&#125;</span><br><span class="line">WSGIApplicationGroup %&#123;GLOBAL&#125;</span><br><span class="line"></span><br><span class="line">&lt;Location <span class="string">"/secret"</span>&gt;</span><br><span class="line">    AuthType Basic</span><br><span class="line">    AuthName <span class="string">"Top Secret"</span></span><br><span class="line">    AuthBasicProvider wsgi</span><br><span class="line">    WSGIAuthUserScript /path/<span class="keyword">to</span>/mysite.<span class="keyword">com</span>/mysite/wsgi.<span class="keyword">py</span></span><br><span class="line">    WSGIAuthGroupScript /path/<span class="keyword">to</span>/mysite.<span class="keyword">com</span>/mysite/wsgi.<span class="keyword">py</span></span><br><span class="line">    Require group secret-agents</span><br><span class="line">    Require valid-user</span><br><span class="line">&lt;/Location&gt;</span><br></pre></td></tr></table></figure>
<p>要支持 <code>WSGIAuthGroupScript</code> 指令，同样的 WSGI 脚本 <code>mysite.wsgi</code> 必须也导入 <code>groups_for_user</code> 函数，函数会返回用户所属用户组的列表。</p>
<figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">from django<span class="selector-class">.contrib</span><span class="selector-class">.auth</span><span class="selector-class">.handlers</span><span class="selector-class">.modwsgi</span> import check_password, groups_for_user</span><br></pre></td></tr></table></figure>
<p>对 <code>/secret</code> 的请求现在也会要求用户是 “secret-agents” 用户组的成员。</p>
<p>23.4 如何使用 Gunicorn 托管 Django</p>
<p><a href="https://gunicorn.org/" target="_blank" rel="noopener">Gunicorn</a> (‘Green Unicorn’) 是一个 UNIX 下的纯 Python WSGI 服务器。它没有其它依赖，容易安装和使用。安装 gunicorn 非常简单，只要执行 <code>pip install gunicorn</code> 即可。</p>
<p>安装 Gunicorn 之后，可以使用 <code>gunicorn</code> 命令启动 Gunicorn 服务进程。最简模式下，只需要把包含了 WSGI 应用对象的 application 模块位置告诉 gunicorn，就可以启动了。因此对于典型的 Django 项目，像这样来调用 gunicorn:</p>
<figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">gunicorn</span> <span class="selector-tag">myproject</span><span class="selector-class">.wsgi</span></span><br></pre></td></tr></table></figure>
<p>这样会创建一个进程，包含了一个监听在 <code>127.0.0.1:8000</code> 的线程。前提是你的项目在 Python path 中，要满足这个条件，最简单的方法是在 <code>manage.py</code> 文件所在的目录中运行这条命令。</p>
<p>23.5 如何用 uWSGI 托管 Django</p>
<p><a href="https://uwsgi-docs.readthedocs.io/" target="_blank" rel="noopener">uWSGI</a> 是一个快速的，自我驱动的，对开发者和系统管理员友好的应用容器服务器，完全由 C 编写。uWSGI 百科介绍了几种 <a href="https://uwsgi-docs.readthedocs.io/en/latest/Install.html" target="_blank" rel="noopener">安装流程</a>。Pip （Python 包管理器）能让你仅用一行代码就安装任意版本的 uWSGI。例子：</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> Install current stable version.</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> pip install uwsgi</span></span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> Or install LTS (long term support).</span></span><br><span class="line"><span class="meta">$</span><span class="bash"> pip install https://projects.unbit.it/downloads/uwsgi-lts.tar.gz</span></span><br></pre></td></tr></table></figure>
<p>假设你有个叫做 <code>mysite</code> 的顶级项目包，期中包含一个模板 <code>mysite/wsgi.py</code>，模块包含一个 WSGI <code>application</code> 对象。如果你使用的是较新的 Django，这就是你运行 <code>django-admin startproject mysite</code> （使用你的项目名替换 <code>mysite</code>）后得到的目录结构。若该文件不存在，你需要创建它。参考文档 <a href="https://docs.djangoproject.com/zh-hans/2.2/howto/deployment/wsgi/" target="_blank" rel="noopener">如何使用 WSGI 进行部署</a> 看看你需要配置的默认内容，以及你还能添加什么。</p>
<p>Django 指定的参数如下：</p>
<ul>
<li><code>chdir</code>：需要包含于 Python 的导入路径的目录的路径——例如，包含 <code>mysite</code> 包的目录。</li>
<li><code>module</code>：要使用的 WSGI 模块——可能是 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/django-admin/#django-admin-startproject" target="_blank" rel="noopener"><code>startproject</code></a> 创建的 <code>mysite.wsgi</code>的模块。</li>
<li><code>env</code>：至少要包括 <code>DJANGO_SETTINGS_MODULE</code>。</li>
<li><code>home</code>: 可选的路径，指向你工程的 virtualenv。</li>
</ul>
<p>示例 ini 配置文件:</p>
<figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">[uwsgi]</span></span><br><span class="line"><span class="attr">chdir</span>=/path/to/your/project</span><br><span class="line"><span class="attr">module</span>=mysite.wsgi:application</span><br><span class="line"><span class="attr">master</span>=<span class="literal">True</span></span><br><span class="line"><span class="attr">pidfile</span>=/tmp/project-master.pid</span><br><span class="line"><span class="attr">vacuum</span>=<span class="literal">True</span></span><br><span class="line"><span class="attr">max-requests</span>=<span class="number">5000</span></span><br><span class="line"><span class="attr">daemonize</span>=/var/log/uwsgi/yourproject.log</span><br></pre></td></tr></table></figure>
<p>示例 ini 配置文件语法:</p>
<figure class="highlight ada"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">uwsgi <span class="comment">--ini uwsgi.ini</span></span><br></pre></td></tr></table></figure>
<p>（24）Django FAQ0</p>
<ul>
<li><p>发音：Django 发音为 <strong>JANG</strong>，使用 FANG 来押韵，字母 “D “是不发声的.</p>
<p>我们也记录了一段 <a href="https://www.red-bean.com/~adrian/django_pronunciation.mp3" target="_blank" rel="noopener">发音的音频片段</a>.</p>
</li>
<li><p>稳定性：相当稳定。使用 Django 搭建的网站能承受每秒 50000 次点击的流量峰值。</p>
</li>
<li><p>扩展性：可以在任何级别添加硬件——数据库服务器，缓存服务器或 Web /应用程序服务器。</p>
</li>
<li><p>MVC 框架：在我们对 MVC 的解释中，“视图”描述了呈现给用户的数据。数据看起来怎么样并不重要，重要的是哪些数据被呈现。 Django  “MTV “ 框架—即 “模型(Model) “、 “模板(Template)” 和 “视图(View). “视图(view)”是 Python 中针对一个特定 URL 的回调函数，此回调函数描述了需要展示的数据。展示效果就是模板。在 Django 里面，一个视图（view）描述了哪些数据会被展示。那控制器（Controller）在什么位置？在 Django 中，会根据 Django 的 URL 配置，将请求分发到适当的视图（view）。</p>
</li>
<li><p>数据库：官方文档推荐PostgreSQL</p>
</li>
<li><p>版本选择：Django2.1,2.2支持python3.5+。Django 的第三方插件可以自由设置他们的版本要求。生产中使用 Django，你应该使用稳定版本。</p>
</li>
<li><p>使用图片和文件字段。在模型中使用 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/models/fields/#django.db.models.FileField" target="_blank" rel="noopener"><code>FileField</code></a> 或 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/models/fields/#django.db.models.ImageField" target="_blank" rel="noopener"><code>ImageField</code></a> ，你还需要完成如下步骤：</p>
<ol>
<li>在你的 setting 文件中，你需要定义：setting: MEDIA_ROOT 作为 Django 存储上传文件目录的完整路径。（为了提高性能，这些文件不会储存在数据库中）定义： setting: MEDIA_URL 作为该目录的基本公共 URL， 确保该目录能够被 Web 服务器的账户写入。</li>
<li>在你的模型中添加 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/models/fields/#django.db.models.FileField" target="_blank" rel="noopener"><code>FileField</code></a> 或者 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/models/fields/#django.db.models.ImageField" target="_blank" rel="noopener"><code>ImageField</code></a> ，可以通过定义:attr:~django.db.models.FileField.upload_to 在 <a href="https://docs.djangoproject.com/zh-hans/2.2/ref/settings/#std:setting-MEDIA_ROOT" target="_blank" rel="noopener"><code>MEDIA_ROOT</code></a> 中明确一个子目录用来上传文件。</li>
<li>所有将被储存在数据库中的文件路径相同（相对于：setting: MEDIA_ROOT<code>）。你很想用由 Django 提供的：attr:~django.db.models.fields.files.FieldFile.url，比如， 如果：class:~django.db.models.ImageField 被叫做</code>mug_shot<code>， 你就可以得到</code>` 图片模板的绝对路径。</li>
</ol>
</li>
</ul>
<ul>
<li><p>导入已有数据库：<a href="https://docs.djangoproject.com/zh-hans/2.2/howto/legacy-databases/" target="_blank" rel="noopener">https://docs.djangoproject.com/zh-hans/2.2/howto/legacy-databases/</a></p>
</li>
<li><p>django只支持单列主键</p>
</li>
<li><p>Django 官方不支持 NoSQL 数据库</p>
</li>
</ul>
<h2 id="技术交流共享QQ群"><a href="#技术交流共享QQ群" class="headerlink" title="技术交流共享QQ群"></a>技术交流共享QQ群</h2><p><font style="color:red">【<strong>机器学习和自然语言QQ群：436303759</strong>】</font>：</p>
<p>机器学习和自然语言（QQ群号：436303759）是一个研究深度学习、机器学习、自然语言处理、数据挖掘、图像处理、目标检测、数据科学等AI相关领域的技术群。其宗旨是纯粹的AI技术圈子、绿色的交流环境。本群禁止有违背法律法规和道德的言谈举止。群成员备注格式：城市-自命名。微信订阅号：datathinks</p>

      
    </div>

    

    
    
    

    
      <div>
        <div id="wechat_subscriber" style="display: block; padding: 10px 0; margin: 20px auto; width: 100%; text-align: center">
    <img id="wechat_subscriber_qcode" src="/uploads/wechat.png" alt="白宁超 wechat" style="width: 200px; max-width: 100%;"/>
    <div>扫一扫关注微信公众号，机器学习和自然语言处理，订阅号datathinks！</div>
</div>

      </div>
    

    
      <div>
        <div style="padding: 10px 0; margin: 20px auto; width: 90%; text-align: center;">
  <div></div>
  <button id="rewardButton" disable="enable" onclick="var qr = document.getElementById('QR'); if (qr.style.display === 'none') {qr.style.display='block';} else {qr.style.display='none'}">
    <span>打赏</span>
  </button>
  <div id="QR" style="display: none;">

    
      <div id="wechat" style="display: inline-block">
        <img id="wechat_qr" src="/images/wechatpay.jpg" alt="白宁超 微信支付"/>
        <p>微信支付</p>
      </div>
    

    
      <div id="alipay" style="display: inline-block">
        <img id="alipay_qr" src="/images/alipay.jpg" alt="白宁超 支付宝"/>
        <p>支付宝</p>
      </div>
    

    

  </div>
</div>

      </div>
    

    

    <footer class="post-footer">
      
        <div class="post-tags">
          
            <a href="/tags/django/" rel="tag"><i class="fa fa-tag"></i> django</a>
          
        </div>
      

      
      
        <div class="post-widgets">
        

        

        
          
          <div class="social_share">
            
               <div>
                 
  <div class="bdsharebuttonbox">
    <a href="#" class="bds_tsina" data-cmd="tsina" title="分享到新浪微博"></a>
    <a href="#" class="bds_douban" data-cmd="douban" title="分享到豆瓣网"></a>
    <a href="#" class="bds_sqq" data-cmd="sqq" title="分享到QQ好友"></a>
    <a href="#" class="bds_qzone" data-cmd="qzone" title="分享到QQ空间"></a>
    <a href="#" class="bds_weixin" data-cmd="weixin" title="分享到微信"></a>
    <a href="#" class="bds_tieba" data-cmd="tieba" title="分享到百度贴吧"></a>
    <a href="#" class="bds_twi" data-cmd="twi" title="分享到Twitter"></a>
    <a href="#" class="bds_fbook" data-cmd="fbook" title="分享到Facebook"></a>
    <a href="#" class="bds_more" data-cmd="more"></a>
    <a class="bds_count" data-cmd="count"></a>
  </div>
  <script>
    window._bd_share_config = {
      "common": {
        "bdText": "",
        "bdMini": "2",
        "bdMiniList": false,
        "bdPic": ""
      },
      "share": {
        "bdSize": "16",
        "bdStyle": "0"
      },
      "image": {
        "viewList": ["tsina", "douban", "sqq", "qzone", "weixin", "twi", "fbook"],
        "viewText": "分享到：",
        "viewSize": "16"
      }
    }
  </script>

<script>
  with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='/static/api/js/share.js?cdnversion='+~(-new Date()/36e5)];
</script>

               </div>
            
            
               <div id="needsharebutton-postbottom">
                 <span class="btn">
                    <i class="fa fa-share-alt" aria-hidden="true"></i>
                 </span>
               </div>
            
          </div>
        
        </div>
      
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/2019/06/13/笔记8-Django基础篇/" rel="next" title="笔记8:Django基础篇">
                <i class="fa fa-chevron-left"></i> 笔记8:Django基础篇
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/2019/06/14/笔记10：PGSQL学习篇/" rel="prev" title="笔记10：PostgreSQL学习篇">
                笔记10：PostgreSQL学习篇 <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </div>
  
  
  
  </article>


  </div>


          </div>
          

  
    <div class="comments" id="comments">
      <div id="lv-container" data-id="city" data-uid="MTAyMC8zOTc5NC8xNjMyMQ=="></div>
    </div>

  
 





        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview-wrap">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview-wrap sidebar-panel">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
            
              <img class="site-author-image" itemprop="image"
                src="/../images/header.png"
                alt="白宁超" />
            
              <p class="site-author-name" itemprop="name">白宁超</p>
              <p class="site-description motion-element" itemprop="description">本站主要研究深度学习、机器学习、自然语言处理等前沿技术。ML&NLP交流群：436303759 <span><a target="_blank" href="http://shang.qq.com/wpa/qunwpa?idkey=ef3bbb679b06ac59b136c57ba9e7935ff9d3b10faeabde6e4efcafe523bbbf4d"><img border="0" src="http://pub.idqqimg.com/wpa/images/group.png" alt="自然语言处理和机器学习技术QQ交流：436303759 " title="自然语言处理和机器学习技术交流"></a></span></p>
          </div>

          
            <nav class="site-state motion-element">
              
                <div class="site-state-item site-state-posts">
                
                  <a href="/archives">
                
                    <span class="site-state-item-count">65</span>
                    <span class="site-state-item-name">日志</span>
                  </a>
                </div>
              

              
                
                
                <div class="site-state-item site-state-categories">
                  <a href="/categories/index.html">
                    
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                    <span class="site-state-item-count">29</span>
                    <span class="site-state-item-name">分类</span>
                  </a>
                </div>
              

              
                
                
                <div class="site-state-item site-state-tags">
                  <a href="/tags/index.html">
                    
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                    <span class="site-state-item-count">119</span>
                    <span class="site-state-item-name">标签</span>
                  </a>
                </div>
              
            </nav>
          

          
            <div class="feed-link motion-element">
              <a href="/atom.xml" rel="alternate">
                <i class="fa fa-rss"></i>
                RSS
              </a>
            </div>
          

          
            <div class="links-of-author motion-element">
              
                <span class="links-of-author-item">
                  <a href="https://github.com/bainingchao" target="_blank" title="GitHub" rel="external nofollow"><i class="fa fa-fw fa-github"></i>GitHub</a>
                  
                </span>
              
                <span class="links-of-author-item">
                  <a href="https://www.google.com.hk/" target="_blank" title="Google" rel="external nofollow"><i class="fa fa-fw fa-google"></i>Google</a>
                  
                </span>
              
                <span class="links-of-author-item">
                  <a href="https://www.baidu.com/" target="_blank" title="百度" rel="external nofollow"><i class="fa fa-fw fa-globe"></i>百度</a>
                  
                </span>
              
                <span class="links-of-author-item">
                  <a href="https://weibo.com/p/1005056002073632?is_all=1" target="_blank" title="微博" rel="external nofollow"><i class="fa fa-fw fa-weibo"></i>微博</a>
                  
                </span>
              
                <span class="links-of-author-item">
                  <a href="http://www.cnblogs.com/baiboy/" target="_blank" title="博客园" rel="external nofollow"><i class="fa fa-fw fa-globe"></i>博客园</a>
                  
                </span>
              
                <span class="links-of-author-item">
                  <a href="https://mp.weixin.qq.com/s/s97I4gtEJIt5rMivWMkPkQ" target="_blank" title="微信公众号" rel="external nofollow"><i class="fa fa-fw fa-weixin"></i>微信公众号</a>
                  
                </span>
              
            </div>
          

          
          

          
          

          
            
          
          

        </div>
      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-3"><a class="nav-link" href="#利用-mod-wsgi-和-Django-用户组-groups-进行授权¶"><span class="nav-number">1.</span> <span class="nav-text">利用 mod_wsgi 和 Django 用户组(groups)进行授权¶</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#技术交流共享QQ群"><span class="nav-number"></span> <span class="nav-text">技术交流共享QQ群</span></a></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <script async src="https://dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js">
</script>

<div class="copyright">&copy; <span itemprop="copyrightYear">2019</span>
  <span class="with-love" id="animate">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">白宁超</span>

  

  
</div>




  



  <!--<div class="powered-by">由 <a class="theme-link" target="_blank" rel="external nofollow" href="https://hexo.io">Hexo</a> 强力驱动 v3.7.1</div> -->



   <!--<span class="post-meta-divider">|</span>-->



   <!--<div class="theme-info">主题 – <a class="theme-link" target="_blank" rel="external nofollow" href="https://theme-next.org">NexT.Gemini</a> v6.4.1</div>-->




        <script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>



<div class="busuanzi-count">
  
    <span class="site-uv" title="总访客量">
      <i class="fa fa-user"></i>
      <span class="busuanzi-value" id="busuanzi_value_site_uv"></span>
    </span>
  

  
    <span class="site-pv" title="总访问量">
      <i class="fa fa-eye"></i>
      <span class="busuanzi-value" id="busuanzi_value_site_pv"></span>
    </span>
  
</div>









        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

    
	
    

    
  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>


























  
  
    <script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  


  


  <script type="text/javascript" src="/js/src/utils.js?v=6.4.1"></script>

  <script type="text/javascript" src="/js/src/motion.js?v=6.4.1"></script>



  
  


  <script type="text/javascript" src="/js/src/affix.js?v=6.4.1"></script>

  <script type="text/javascript" src="/js/src/schemes/pisces.js?v=6.4.1"></script>



  
  <script type="text/javascript" src="/js/src/scrollspy.js?v=6.4.1"></script>
<script type="text/javascript" src="/js/src/post-details.js?v=6.4.1"></script>



  


  <script type="text/javascript" src="/js/src/bootstrap.js?v=6.4.1"></script>



  



  
    <script type="text/javascript">
      window.livereOptions = {
        refer: '2019/06/13/笔记9-Django提升篇/'
      };
      (function(d, s) {
        var j, e = d.getElementsByTagName(s)[0];
        if (typeof LivereTower === 'function') { return; }
        j = d.createElement(s);
        j.src = 'https://cdn-city.livere.com/js/embed.dist.js';
        j.async = true;
        e.parentNode.insertBefore(j, e);
      })(document, 'script');
    </script>
  










  

  <script type="text/javascript">
    // Popup Window;
    var isfetched = false;
    var isXml = true;
    // Search DB path;
    var search_path = "search.xml";
    if (search_path.length === 0) {
      search_path = "search.xml";
    } else if (/json$/i.test(search_path)) {
      isXml = false;
    }
    var path = "/" + search_path;
    // monitor main search box;

    var onPopupClose = function (e) {
      $('.popup').hide();
      $('#local-search-input').val('');
      $('.search-result-list').remove();
      $('#no-result').remove();
      $(".local-search-pop-overlay").remove();
      $('body').css('overflow', '');
    }

    function proceedsearch() {
      $("body")
        .append('<div class="search-popup-overlay local-search-pop-overlay"></div>')
        .css('overflow', 'hidden');
      $('.search-popup-overlay').click(onPopupClose);
      $('.popup').toggle();
      var $localSearchInput = $('#local-search-input');
      $localSearchInput.attr("autocapitalize", "none");
      $localSearchInput.attr("autocorrect", "off");
      $localSearchInput.focus();
    }

    // search function;
    var searchFunc = function(path, search_id, content_id) {
      'use strict';

      // start loading animation
      $("body")
        .append('<div class="search-popup-overlay local-search-pop-overlay">' +
          '<div id="search-loading-icon">' +
          '<i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>' +
          '</div>' +
          '</div>')
        .css('overflow', 'hidden');
      $("#search-loading-icon").css('margin', '20% auto 0 auto').css('text-align', 'center');

      

      $.ajax({
        url: path,
        dataType: isXml ? "xml" : "json",
        async: true,
        success: function(res) {
          // get the contents from search data
          isfetched = true;
          $('.popup').detach().appendTo('.header-inner');
          var datas = isXml ? $("entry", res).map(function() {
            return {
              title: $("title", this).text(),
              content: $("content",this).text(),
              url: $("url" , this).text()
            };
          }).get() : res;
          var input = document.getElementById(search_id);
          var resultContent = document.getElementById(content_id);
          var inputEventFunction = function() {
            var searchText = input.value.trim().toLowerCase();
            var keywords = searchText.split(/[\s\-]+/);
            if (keywords.length > 1) {
              keywords.push(searchText);
            }
            var resultItems = [];
            if (searchText.length > 0) {
              // perform local searching
              datas.forEach(function(data) {
                var isMatch = false;
                var hitCount = 0;
                var searchTextCount = 0;
                var title = data.title.trim();
                var titleInLowerCase = title.toLowerCase();
                var content = data.content.trim().replace(/<[^>]+>/g,"");
                
                var contentInLowerCase = content.toLowerCase();
                var articleUrl = decodeURIComponent(data.url);
                var indexOfTitle = [];
                var indexOfContent = [];
                // only match articles with not empty titles
                if(title != '') {
                  keywords.forEach(function(keyword) {
                    function getIndexByWord(word, text, caseSensitive) {
                      var wordLen = word.length;
                      if (wordLen === 0) {
                        return [];
                      }
                      var startPosition = 0, position = [], index = [];
                      if (!caseSensitive) {
                        text = text.toLowerCase();
                        word = word.toLowerCase();
                      }
                      while ((position = text.indexOf(word, startPosition)) > -1) {
                        index.push({position: position, word: word});
                        startPosition = position + wordLen;
                      }
                      return index;
                    }

                    indexOfTitle = indexOfTitle.concat(getIndexByWord(keyword, titleInLowerCase, false));
                    indexOfContent = indexOfContent.concat(getIndexByWord(keyword, contentInLowerCase, false));
                  });
                  if (indexOfTitle.length > 0 || indexOfContent.length > 0) {
                    isMatch = true;
                    hitCount = indexOfTitle.length + indexOfContent.length;
                  }
                }

                // show search results

                if (isMatch) {
                  // sort index by position of keyword

                  [indexOfTitle, indexOfContent].forEach(function (index) {
                    index.sort(function (itemLeft, itemRight) {
                      if (itemRight.position !== itemLeft.position) {
                        return itemRight.position - itemLeft.position;
                      } else {
                        return itemLeft.word.length - itemRight.word.length;
                      }
                    });
                  });

                  // merge hits into slices

                  function mergeIntoSlice(text, start, end, index) {
                    var item = index[index.length - 1];
                    var position = item.position;
                    var word = item.word;
                    var hits = [];
                    var searchTextCountInSlice = 0;
                    while (position + word.length <= end && index.length != 0) {
                      if (word === searchText) {
                        searchTextCountInSlice++;
                      }
                      hits.push({position: position, length: word.length});
                      var wordEnd = position + word.length;

                      // move to next position of hit

                      index.pop();
                      while (index.length != 0) {
                        item = index[index.length - 1];
                        position = item.position;
                        word = item.word;
                        if (wordEnd > position) {
                          index.pop();
                        } else {
                          break;
                        }
                      }
                    }
                    searchTextCount += searchTextCountInSlice;
                    return {
                      hits: hits,
                      start: start,
                      end: end,
                      searchTextCount: searchTextCountInSlice
                    };
                  }

                  var slicesOfTitle = [];
                  if (indexOfTitle.length != 0) {
                    slicesOfTitle.push(mergeIntoSlice(title, 0, title.length, indexOfTitle));
                  }

                  var slicesOfContent = [];
                  while (indexOfContent.length != 0) {
                    var item = indexOfContent[indexOfContent.length - 1];
                    var position = item.position;
                    var word = item.word;
                    // cut out 100 characters
                    var start = position - 20;
                    var end = position + 80;
                    if(start < 0){
                      start = 0;
                    }
                    if (end < position + word.length) {
                      end = position + word.length;
                    }
                    if(end > content.length){
                      end = content.length;
                    }
                    slicesOfContent.push(mergeIntoSlice(content, start, end, indexOfContent));
                  }

                  // sort slices in content by search text's count and hits' count

                  slicesOfContent.sort(function (sliceLeft, sliceRight) {
                    if (sliceLeft.searchTextCount !== sliceRight.searchTextCount) {
                      return sliceRight.searchTextCount - sliceLeft.searchTextCount;
                    } else if (sliceLeft.hits.length !== sliceRight.hits.length) {
                      return sliceRight.hits.length - sliceLeft.hits.length;
                    } else {
                      return sliceLeft.start - sliceRight.start;
                    }
                  });

                  // select top N slices in content

                  var upperBound = parseInt('1');
                  if (upperBound >= 0) {
                    slicesOfContent = slicesOfContent.slice(0, upperBound);
                  }

                  // highlight title and content

                  function highlightKeyword(text, slice) {
                    var result = '';
                    var prevEnd = slice.start;
                    slice.hits.forEach(function (hit) {
                      result += text.substring(prevEnd, hit.position);
                      var end = hit.position + hit.length;
                      result += '<b class="search-keyword">' + text.substring(hit.position, end) + '</b>';
                      prevEnd = end;
                    });
                    result += text.substring(prevEnd, slice.end);
                    return result;
                  }

                  var resultItem = '';

                  if (slicesOfTitle.length != 0) {
                    resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + highlightKeyword(title, slicesOfTitle[0]) + "</a>";
                  } else {
                    resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + title + "</a>";
                  }

                  slicesOfContent.forEach(function (slice) {
                    resultItem += "<a href='" + articleUrl + "'>" +
                      "<p class=\"search-result\">" + highlightKeyword(content, slice) +
                      "...</p>" + "</a>";
                  });

                  resultItem += "</li>";
                  resultItems.push({
                    item: resultItem,
                    searchTextCount: searchTextCount,
                    hitCount: hitCount,
                    id: resultItems.length
                  });
                }
              })
            };
            if (keywords.length === 1 && keywords[0] === "") {
              resultContent.innerHTML = '<div id="no-result"><i class="fa fa-search fa-5x" /></div>'
            } else if (resultItems.length === 0) {
              resultContent.innerHTML = '<div id="no-result"><i class="fa fa-frown-o fa-5x" /></div>'
            } else {
              resultItems.sort(function (resultLeft, resultRight) {
                if (resultLeft.searchTextCount !== resultRight.searchTextCount) {
                  return resultRight.searchTextCount - resultLeft.searchTextCount;
                } else if (resultLeft.hitCount !== resultRight.hitCount) {
                  return resultRight.hitCount - resultLeft.hitCount;
                } else {
                  return resultRight.id - resultLeft.id;
                }
              });
              var searchResultList = '<ul class=\"search-result-list\">';
              resultItems.forEach(function (result) {
                searchResultList += result.item;
              })
              searchResultList += "</ul>";
              resultContent.innerHTML = searchResultList;
            }
          }

          if ('auto' === 'auto') {
            input.addEventListener('input', inputEventFunction);
          } else {
            $('.search-icon').click(inputEventFunction);
            input.addEventListener('keypress', function (event) {
              if (event.keyCode === 13) {
                inputEventFunction();
              }
            });
          }

          // remove loading animation
          $(".local-search-pop-overlay").remove();
          $('body').css('overflow', '');

          proceedsearch();
        }
      });
    }

    // handle and trigger popup window;
    $('.popup-trigger').click(function(e) {
      e.stopPropagation();
      if (isfetched === false) {
        searchFunc(path, 'local-search-input', 'local-search-result');
      } else {
        proceedsearch();
      };
    });

    $('.popup-btn-close').click(onPopupClose);
    $('.popup').click(function(e){
      e.stopPropagation();
    });
    $(document).on('keyup', function (event) {
      var shouldDismissSearchPopup = event.which === 27 &&
        $('.search-popup').is(':visible');
      if (shouldDismissSearchPopup) {
        onPopupClose();
      }
    });
  </script>





  

  

  
<script>
(function(){
    var bp = document.createElement('script');
    var curProtocol = window.location.protocol.split(':')[0];
    if (curProtocol === 'https') {
        bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';        
    }
    else {
        bp.src = 'http://push.zhanzhang.baidu.com/push.js';
    }
    var s = document.getElementsByTagName("script")[0];
    s.parentNode.insertBefore(bp, s);
})();
</script>


  
  

  
  

  


  
  
  
  <script src="/lib/needsharebutton/needsharebutton.js"></script>

  <script>
    
      pbOptions = {};
      
          pbOptions.iconStyle = "box";
      
          pbOptions.boxForm = "horizontal";
      
          pbOptions.position = "bottomCenter";
      
          pbOptions.networks = "Weibo,Wechat,Douban,QQZone,Linkedin,Facebook";
      
      new needShareButton('#needsharebutton-postbottom', pbOptions);
    
    
  </script>

  

  

  

  

  

  

  <!-- 页面点击小红心 -->
	<script type="text/javascript" src="../js/src/love.js"></script><!-- hexo-inject:begin --><!-- Begin: Injected MathJax -->
<script type="text/x-mathjax-config">
  MathJax.Hub.Config({"tex2jax":{"inlineMath":[["$","$"],["\\(","\\)"]],"skipTags":["script","noscript","style","textarea","pre","code"],"processEscapes":true},"TeX":{"equationNumbers":{"autoNumber":"AMS"}}});
</script>

<script type="text/x-mathjax-config">
  MathJax.Hub.Queue(function() {
    var all = MathJax.Hub.getAllJax(), i;
    for(i=0; i < all.length; i += 1) {
      all[i].SourceElement().parentNode.className += ' has-jax';
    }
  });
</script>

<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js">
</script>
<!-- End: Injected MathJax -->
<!-- hexo-inject:end -->
</body>
</html>
